mirror of
https://github.com/dzeiocom/OpenHealth.git
synced 2025-04-25 12:22:15 +00:00
feat: updated
This commit is contained in:
parent
7875271b7e
commit
357024770a
@ -130,7 +130,7 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Dzeio Charts
|
// Dzeio Charts
|
||||||
implementation("com.dzeio:charts:edd78e87e1")
|
implementation("com.dzeio:charts:cbd5f57f8d")
|
||||||
|
|
||||||
// Dzeio Crash Handler
|
// Dzeio Crash Handler
|
||||||
implementation("com.dzeio:crashhandler:1.0.1")
|
implementation("com.dzeio:crashhandler:1.0.1")
|
||||||
@ -139,7 +139,7 @@ dependencies {
|
|||||||
implementation("androidx.core:core-ktx:1.9.0")
|
implementation("androidx.core:core-ktx:1.9.0")
|
||||||
implementation("androidx.appcompat:appcompat:1.7.0-alpha01")
|
implementation("androidx.appcompat:appcompat:1.7.0-alpha01")
|
||||||
implementation("javax.inject:javax.inject:1")
|
implementation("javax.inject:javax.inject:1")
|
||||||
implementation("com.google.android.material:material:1.8.0-rc01")
|
implementation("com.google.android.material:material:1.9.0-alpha01")
|
||||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||||
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.5.1")
|
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.5.1")
|
||||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1")
|
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1")
|
||||||
@ -169,18 +169,15 @@ dependencies {
|
|||||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||||
|
|
||||||
// Graph
|
|
||||||
implementation("com.github.PhilJay:MPAndroidChart:v3.1.0")
|
|
||||||
|
|
||||||
// Hilt
|
// Hilt
|
||||||
implementation("com.google.dagger:hilt-android:2.44.2")
|
implementation("com.google.dagger:hilt-android:2.44.2")
|
||||||
kapt("com.google.dagger:hilt-compiler:2.44.2")
|
kapt("com.google.dagger:hilt-compiler:2.44.2")
|
||||||
|
|
||||||
// ROOM
|
// ROOM
|
||||||
implementation("androidx.room:room-runtime:2.4.3")
|
implementation("androidx.room:room-runtime:2.5.0")
|
||||||
kapt("androidx.room:room-compiler:2.4.3")
|
kapt("androidx.room:room-compiler:2.5.0")
|
||||||
implementation("androidx.room:room-ktx:2.4.3")
|
implementation("androidx.room:room-ktx:2.5.0")
|
||||||
testImplementation("androidx.room:room-testing:2.4.3")
|
testImplementation("androidx.room:room-testing:2.5.0")
|
||||||
|
|
||||||
// Futures
|
// Futures
|
||||||
implementation("com.google.guava:guava:31.1-jre")
|
implementation("com.google.guava:guava:31.1-jre")
|
||||||
|
@ -1,150 +0,0 @@
|
|||||||
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.ChartUtils
|
|
||||||
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
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: Migrate to DzeioCharts when it is ready
|
|
||||||
*
|
|
||||||
* Setup the mikephil Chart for the Weight
|
|
||||||
*/
|
|
||||||
object WeightChart {
|
|
||||||
fun setup(
|
|
||||||
chart: LineChart,
|
|
||||||
view: View,
|
|
||||||
data: List<Weight>,
|
|
||||||
modifier: Units.Mass,
|
|
||||||
goal: Float?,
|
|
||||||
limit: Boolean = true
|
|
||||||
) {
|
|
||||||
ChartUtils.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 = ChartUtils.lineDataSet(
|
|
||||||
LineDataSet(
|
|
||||||
data.mapIndexed { _, weight ->
|
|
||||||
return@mapIndexed Entry(
|
|
||||||
weight.timestamp.toFloat(),
|
|
||||||
weight.weight * modifier.modifier
|
|
||||||
)
|
|
||||||
},
|
|
||||||
"Weight"
|
|
||||||
)
|
|
||||||
).apply {
|
|
||||||
axisDependency = YAxis.AxisDependency.RIGHT
|
|
||||||
}
|
|
||||||
|
|
||||||
val averageData = ChartUtils.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.removeAllLimitLines()
|
|
||||||
axisRight.addLimitLine(limit)
|
|
||||||
}
|
|
||||||
invalidate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,7 +3,6 @@ package com.dzeio.openhealth.ui
|
|||||||
import android.app.NotificationChannel
|
import android.app.NotificationChannel
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@ -11,6 +10,7 @@ import android.util.Log
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
|
import android.view.WindowInsets
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
@ -21,7 +21,6 @@ import androidx.navigation.ui.NavigationUI
|
|||||||
import androidx.navigation.ui.navigateUp
|
import androidx.navigation.ui.navigateUp
|
||||||
import androidx.navigation.ui.setupActionBarWithNavController
|
import androidx.navigation.ui.setupActionBarWithNavController
|
||||||
import androidx.navigation.ui.setupWithNavController
|
import androidx.navigation.ui.setupWithNavController
|
||||||
import com.dzeio.openhealth.Application
|
|
||||||
import com.dzeio.openhealth.R
|
import com.dzeio.openhealth.R
|
||||||
import com.dzeio.openhealth.core.BaseActivity
|
import com.dzeio.openhealth.core.BaseActivity
|
||||||
import com.dzeio.openhealth.databinding.ActivityMainBinding
|
import com.dzeio.openhealth.databinding.ActivityMainBinding
|
||||||
@ -35,7 +34,7 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||||||
class MainActivity : BaseActivity<ActivityMainBinding>() {
|
class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "${Application.TAG}/MainActivity"
|
val TAG: String = this::class.java.simpleName
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var appBarConfiguration: AppBarConfiguration
|
private lateinit var appBarConfiguration: AppBarConfiguration
|
||||||
@ -48,7 +47,6 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
setTheme(R.style.Theme_OpenHealth_NoActionBar)
|
setTheme(R.style.Theme_OpenHealth_NoActionBar)
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreated(savedInstanceState: Bundle?) {
|
override fun onCreated(savedInstanceState: Bundle?) {
|
||||||
@ -58,7 +56,6 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
|
|
||||||
// Comportement chelou API 28-
|
// Comportement chelou API 28-
|
||||||
// Comportement normal 31+
|
// Comportement normal 31+
|
||||||
|
|
||||||
// do not do the cool status/navigation bars for API 29 & 30
|
// do not do the cool status/navigation bars for API 29 & 30
|
||||||
if (Build.VERSION.SDK_INT != Build.VERSION_CODES.R && Build.VERSION.SDK_INT != Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT != Build.VERSION_CODES.R && Build.VERSION.SDK_INT != Build.VERSION_CODES.Q) {
|
||||||
// allow to put the content behind the status bar & Navigation bar (one of them at least lul)
|
// allow to put the content behind the status bar & Navigation bar (one of them at least lul)
|
||||||
@ -66,25 +63,23 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
|
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
|
||||||
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||||
|
|
||||||
// Make the color of the navigation bar semi-transparent
|
|
||||||
// window.navigationBarColor = Color.TRANSPARENT
|
|
||||||
// Make the color of the status bar transparent
|
|
||||||
// window.statusBarColor = Color.TRANSPARENT
|
|
||||||
// Apply the previous changes
|
|
||||||
// window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
|
||||||
|
|
||||||
// Update toolbar height with the statusbar size included
|
// Update toolbar height with the statusbar size included
|
||||||
// ALSO: make both the status/navigation bars transparent (WHYYYYYYY)
|
// ALSO: make both the status/navigation bars transparent (WHYYYYYYY)
|
||||||
val toolbarHeight = binding.toolbar.layoutParams.height
|
val toolbarHeight = binding.toolbar.layoutParams.height
|
||||||
window.decorView.setOnApplyWindowInsetsListener { _, insets ->
|
window.decorView.setOnApplyWindowInsetsListener { _, insets ->
|
||||||
val statusBarSize = insets.systemWindowInsetTop
|
// Use getInsets(int) with WindowInsets.Type.systemBars() instead.
|
||||||
|
val statusBarSize = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
insets.getInsets(WindowInsets.Type.systemBars()).top
|
||||||
|
} else {
|
||||||
|
insets.systemWindowInsetTop
|
||||||
|
}
|
||||||
// Add padding to the toolbar (YaY I know how something works)
|
// Add padding to the toolbar (YaY I know how something works)
|
||||||
binding.toolbar.updatePadding(top = statusBarSize)
|
binding.toolbar.updatePadding(top = statusBarSize)
|
||||||
binding.toolbar.layoutParams.height = toolbarHeight + statusBarSize
|
binding.toolbar.layoutParams.height = toolbarHeight + statusBarSize
|
||||||
return@setOnApplyWindowInsetsListener insets
|
return@setOnApplyWindowInsetsListener insets
|
||||||
}
|
}
|
||||||
|
|
||||||
// normally makes sure icons are at the correct color but idk if it works
|
// normally makes sure icons are at the correct color
|
||||||
when (this.resources.configuration.uiMode.and(Configuration.UI_MODE_NIGHT_MASK)) {
|
when (this.resources.configuration.uiMode.and(Configuration.UI_MODE_NIGHT_MASK)) {
|
||||||
Configuration.UI_MODE_NIGHT_YES -> {
|
Configuration.UI_MODE_NIGHT_YES -> {
|
||||||
WindowCompat.getInsetsController(window, window.decorView).apply {
|
WindowCompat.getInsetsController(window, window.decorView).apply {
|
||||||
@ -121,25 +116,17 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
|
|
||||||
binding.bottomNav.setupWithNavController(navController)
|
binding.bottomNav.setupWithNavController(navController)
|
||||||
|
|
||||||
// registerForActivityResult(ActivityResultContracts.RequestPermission()) {
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// .launch(Manifest.permission.ACTIVITY_RECOGNITION)
|
|
||||||
|
|
||||||
createNotificationChannel()
|
createNotificationChannel()
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
WaterReminderWorker.setup(this)
|
WaterReminderWorker.setup(this)
|
||||||
// StepCountService.setup(this)
|
|
||||||
|
|
||||||
ServiceUtils.startService(this, OpenHealthService::class.java)
|
ServiceUtils.startService(this, OpenHealthService::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
super.onCreateOptionsMenu(menu)
|
|
||||||
menuInflater.inflate(R.menu.main, menu)
|
menuInflater.inflate(R.menu.main, menu)
|
||||||
return true
|
return super.onCreateOptionsMenu(menu)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean =
|
override fun onSupportNavigateUp(): Boolean =
|
||||||
@ -149,22 +136,13 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
NavigationUI.onNavDestinationSelected(item, navController) ||
|
NavigationUI.onNavDestinationSelected(item, navController) ||
|
||||||
super.onOptionsItemSelected(item)
|
super.onOptionsItemSelected(item)
|
||||||
|
|
||||||
@Deprecated("Deprecated in Java")
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
|
||||||
Log.d("MainActivity", "onActivityResult $requestCode $resultCode")
|
|
||||||
for (fragment in supportFragmentManager.primaryNavigationFragment!!.childFragmentManager.fragments) {
|
|
||||||
fragment.onActivityResult(requestCode, resultCode, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createNotificationChannel() {
|
private fun createNotificationChannel() {
|
||||||
val notificationManager: NotificationManager =
|
val notificationManager: NotificationManager =
|
||||||
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
for (channel in NotificationChannels.values()) {
|
for (channel in NotificationChannels.values()) {
|
||||||
Log.d("MainActivity", channel.channelName)
|
Log.d(TAG, channel.channelName)
|
||||||
try {
|
try {
|
||||||
notificationManager.createNotificationChannel(
|
notificationManager.createNotificationChannel(
|
||||||
NotificationChannel(
|
NotificationChannel(
|
||||||
@ -174,7 +152,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("MainActivity", "Error Creating Notification Channel", e)
|
Log.e(TAG, "Error Creating Notification Channel", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,15 +9,16 @@ import android.view.LayoutInflater
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.dzeio.charts.Entry
|
||||||
|
import com.dzeio.charts.series.LineSerie
|
||||||
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.WeightDialog
|
import com.dzeio.openhealth.ui.weight.WeightDialog
|
||||||
import com.dzeio.openhealth.units.Units
|
import com.dzeio.openhealth.units.Units
|
||||||
import com.dzeio.openhealth.utils.DrawUtils
|
|
||||||
import com.dzeio.openhealth.utils.ChartUtils
|
import com.dzeio.openhealth.utils.ChartUtils
|
||||||
|
import com.dzeio.openhealth.utils.DrawUtils
|
||||||
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 kotlin.math.max
|
import kotlin.math.max
|
||||||
@ -89,18 +90,11 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
|
|||||||
findNavController().navigate(HomeFragmentDirections.actionNavHomeToNavWaterHome())
|
findNavController().navigate(HomeFragmentDirections.actionNavHomeToNavWaterHome())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a line Chart using the graph library
|
binding.weightGraph.apply {
|
||||||
ChartUtils.lineChartSetup(
|
val serie = LineSerie(this)
|
||||||
binding.weightGraph,
|
ChartUtils.materielTheme(this, requireView())
|
||||||
MaterialColors.getColor(
|
series = arrayListOf(serie)
|
||||||
requireView(),
|
}
|
||||||
com.google.android.material.R.attr.colorPrimary
|
|
||||||
),
|
|
||||||
MaterialColors.getColor(
|
|
||||||
requireView(),
|
|
||||||
com.google.android.material.R.attr.colorOnBackground
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Update the water intake Graph when the water intake changes
|
// Update the water intake Graph when the water intake changes
|
||||||
viewModel.water.observe(viewLifecycleOwner) {
|
viewModel.water.observe(viewLifecycleOwner) {
|
||||||
@ -147,13 +141,28 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
|
|||||||
* Function that update the graph for the weight
|
* Function that update the graph for the weight
|
||||||
*/
|
*/
|
||||||
private fun updateGraph(list: List<Weight>) {
|
private fun updateGraph(list: List<Weight>) {
|
||||||
WeightChart.setup(
|
val chart = binding.weightGraph
|
||||||
binding.weightGraph,
|
val serie = chart.series[0] as LineSerie
|
||||||
requireView(),
|
|
||||||
list,
|
val entries: ArrayList<Entry> = arrayListOf()
|
||||||
viewModel.massUnit.value!!,
|
|
||||||
viewModel.goalWeight.value
|
list.forEach {
|
||||||
)
|
entries.add(
|
||||||
|
Entry(
|
||||||
|
it.timestamp.toDouble(),
|
||||||
|
it.weight
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
serie.entries = entries
|
||||||
|
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
chart.xAxis.x = 0.0
|
||||||
|
} else {
|
||||||
|
chart.xAxis.x = list[0].timestamp.toDouble()
|
||||||
|
}
|
||||||
|
|
||||||
|
chart.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -165,7 +174,6 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
|
|||||||
* function that update the water count in the home page
|
* function that update the water count in the home page
|
||||||
*/
|
*/
|
||||||
private fun updateWater(newValue: Int) {
|
private fun updateWater(newValue: Int) {
|
||||||
|
|
||||||
// get the current Unit
|
// get the current Unit
|
||||||
val waterUnit =
|
val waterUnit =
|
||||||
Units.Volume.find(settings.getString("water_unit", "milliliter") ?: "Milliliter")
|
Units.Volume.find(settings.getString("water_unit", "milliliter") ?: "Milliliter")
|
||||||
@ -193,7 +201,6 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
|
|||||||
height = binding.background.height
|
height = binding.background.height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Prepare the update animation
|
// Prepare the update animation
|
||||||
val animator = ValueAnimator.ofInt(
|
val animator = ValueAnimator.ofInt(
|
||||||
this.oldValue.toInt(),
|
this.oldValue.toInt(),
|
||||||
@ -201,7 +208,6 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
|
|||||||
)
|
)
|
||||||
animator.duration = 300 // ms
|
animator.duration = 300 // ms
|
||||||
animator.addUpdateListener {
|
animator.addUpdateListener {
|
||||||
|
|
||||||
this.oldValue = (it.animatedValue as Int).toFloat()
|
this.oldValue = (it.animatedValue as Int).toFloat()
|
||||||
val value = 100 * it.animatedValue as Int / viewModel.dailyWaterIntake.toFloat()
|
val value = 100 * it.animatedValue as Int / viewModel.dailyWaterIntake.toFloat()
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.dzeio.charts.Entry
|
import com.dzeio.charts.Entry
|
||||||
|
import com.dzeio.charts.axis.Line
|
||||||
import com.dzeio.charts.series.BarSerie
|
import com.dzeio.charts.series.BarSerie
|
||||||
import com.dzeio.openhealth.Application
|
import com.dzeio.openhealth.Application
|
||||||
import com.dzeio.openhealth.adapters.StepsAdapter
|
import com.dzeio.openhealth.adapters.StepsAdapter
|
||||||
@ -18,6 +19,7 @@ import java.util.Calendar
|
|||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.TimeZone
|
import java.util.TimeZone
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class StepsHomeFragment :
|
class StepsHomeFragment :
|
||||||
@ -58,27 +60,33 @@ class StepsHomeFragment :
|
|||||||
chart.apply {
|
chart.apply {
|
||||||
ChartUtils.materielTheme(chart, requireView())
|
ChartUtils.materielTheme(chart, requireView())
|
||||||
yAxis.apply {
|
yAxis.apply {
|
||||||
onValueFormat = { value -> "${value.toInt()}" }
|
setYMin(0f)
|
||||||
}
|
}
|
||||||
|
|
||||||
xAxis.apply {
|
xAxis.apply {
|
||||||
dataWidth = 604800000.0
|
dataWidth = 604800000.0
|
||||||
textPaint.textSize = 32f
|
textPaint.textSize = 32f
|
||||||
onValueFormat = onValueFormat@{
|
onValueFormat = onValueFormat@{
|
||||||
val formatter = DateFormat.getDateTimeInstance(
|
val formatter = DateFormat.getDateInstance(
|
||||||
DateFormat.SHORT,
|
|
||||||
DateFormat.SHORT,
|
DateFormat.SHORT,
|
||||||
Locale.getDefault()
|
Locale.getDefault()
|
||||||
)
|
)
|
||||||
return@onValueFormat formatter.format(Date(it.toLong()))
|
return@onValueFormat formatter.format(Date(it.toLong()))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
annotator.annotationTitleFormat = { "${it.y.roundToInt()} steps" }
|
||||||
|
annotator.annotationSubTitleFormat = annotationSubTitleFormat@{
|
||||||
|
val formatter = DateFormat.getDateInstance(
|
||||||
|
DateFormat.SHORT,
|
||||||
|
Locale.getDefault()
|
||||||
|
)
|
||||||
|
return@annotationSubTitleFormat formatter.format(Date(it.x.toLong()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.goal.observe(viewLifecycleOwner) {
|
viewModel.goal.observe(viewLifecycleOwner) {
|
||||||
if (it != null) {
|
if (it != null) {
|
||||||
chart.yAxis.addLine(it.toFloat())
|
chart.yAxis.addLine(it.toFloat(), Line(true))
|
||||||
chart.refresh()
|
chart.refresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,7 +128,6 @@ class StepsHomeFragment :
|
|||||||
|
|
||||||
chart.xAxis.x = chart.xAxis.getXMin()
|
chart.xAxis.x = chart.xAxis.getXMin()
|
||||||
|
|
||||||
|
|
||||||
chart.refresh()
|
chart.refresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,7 @@ class ListWeightFragment :
|
|||||||
|
|
||||||
xAxis.apply {
|
xAxis.apply {
|
||||||
// 7 day history
|
// 7 day history
|
||||||
// increment = (7 * 24 * 60 * 60 * 1000).toDouble()
|
dataWidth = (7 * 24 * 60 * 60 * 1000).toDouble()
|
||||||
textPaint.color = MaterialColors.getColor(
|
textPaint.color = MaterialColors.getColor(
|
||||||
requireView(),
|
requireView(),
|
||||||
com.google.android.material.R.attr.colorOnPrimaryContainer
|
com.google.android.material.R.attr.colorOnPrimaryContainer
|
||||||
@ -124,7 +124,6 @@ class ListWeightFragment :
|
|||||||
)
|
)
|
||||||
return@onValueFormat formatter.format(Date(it.toLong()))
|
return@onValueFormat formatter.format(Date(it.toLong()))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
package com.dzeio.openhealth.utils
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import java.io.File
|
|
||||||
import java.io.RandomAccessFile
|
|
||||||
import java.nio.channels.FileChannel
|
|
||||||
|
|
||||||
object BitmapUtils {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stolen from StackOverflow but I don't remember where...
|
|
||||||
*
|
|
||||||
* Convert an immutable Bitmap to a mutable one if possible
|
|
||||||
*/
|
|
||||||
fun convertToMutable(context: Context, imgIn: Bitmap): Bitmap? {
|
|
||||||
val width = imgIn.width
|
|
||||||
val height = imgIn.height
|
|
||||||
val type = imgIn.config
|
|
||||||
var outputFile: File? = null
|
|
||||||
val outputDir = context.cacheDir
|
|
||||||
try {
|
|
||||||
outputFile = File.createTempFile(
|
|
||||||
System.currentTimeMillis().toString(),
|
|
||||||
null,
|
|
||||||
outputDir
|
|
||||||
)
|
|
||||||
outputFile.deleteOnExit()
|
|
||||||
val randomAccessFile = RandomAccessFile(outputFile, "rw")
|
|
||||||
val channel = randomAccessFile.channel
|
|
||||||
val map = channel.map(
|
|
||||||
FileChannel.MapMode.READ_WRITE,
|
|
||||||
0,
|
|
||||||
(imgIn.rowBytes * height).toLong()
|
|
||||||
)
|
|
||||||
imgIn.copyPixelsToBuffer(map)
|
|
||||||
imgIn.recycle()
|
|
||||||
val result = Bitmap.createBitmap(width, height, type)
|
|
||||||
map.position(0)
|
|
||||||
result.copyPixelsFromBuffer(map)
|
|
||||||
channel.close()
|
|
||||||
randomAccessFile.close()
|
|
||||||
outputFile.delete()
|
|
||||||
return result
|
|
||||||
} catch (e: Exception) {
|
|
||||||
} finally {
|
|
||||||
outputFile?.delete()
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,117 +2,51 @@ package com.dzeio.openhealth.utils
|
|||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import com.dzeio.charts.ChartView
|
import com.dzeio.charts.ChartView
|
||||||
|
import com.dzeio.charts.components.Annotation
|
||||||
import com.dzeio.charts.series.BarSerie
|
import com.dzeio.charts.series.BarSerie
|
||||||
import com.dzeio.charts.series.LineSerie
|
import com.dzeio.charts.series.LineSerie
|
||||||
import com.github.mikephil.charting.charts.BarLineChartBase
|
|
||||||
import com.github.mikephil.charting.charts.LineChart
|
|
||||||
import com.github.mikephil.charting.components.AxisBase
|
|
||||||
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 com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
import java.text.SimpleDateFormat
|
import java.text.DateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
import kotlin.math.roundToLong
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utils object to create graphics using the mikephil charting library
|
* Utils object to create Charts
|
||||||
*
|
|
||||||
* TODO: migrate to DzeioCharts once it is ready
|
|
||||||
*/
|
*/
|
||||||
object ChartUtils {
|
object ChartUtils {
|
||||||
|
|
||||||
fun lineChartSetup(chart: LineChart, mainColor: Int, textColor: Int) {
|
|
||||||
barLineChartSetup(chart, mainColor, textColor)
|
|
||||||
// chart.isAutoScaleMinMaxEnabled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun lineDataSet(lineDataSet: LineDataSet): LineDataSet {
|
|
||||||
return lineDataSet.apply {
|
|
||||||
setDrawCircles(false)
|
|
||||||
setDrawCircleHole(false)
|
|
||||||
mode = LineDataSet.Mode.HORIZONTAL_BEZIER
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T : BarLineScatterCandleBubbleData<out IBarLineScatterCandleBubbleDataSet<out Entry>>?> barLineChartSetup(
|
|
||||||
chart: BarLineChartBase<T>,
|
|
||||||
mainColor: Int,
|
|
||||||
textColor: Int
|
|
||||||
) {
|
|
||||||
chart.apply {
|
|
||||||
// Setup
|
|
||||||
legend.isEnabled = true
|
|
||||||
description = Description().apply { isEnabled = false }
|
|
||||||
|
|
||||||
xAxis.apply {
|
|
||||||
valueFormatter = DateValueFormatter()
|
|
||||||
position = XAxis.XAxisPosition.BOTTOM
|
|
||||||
setDrawGridLines(false)
|
|
||||||
setLabelCount(3, true)
|
|
||||||
this.textColor = textColor
|
|
||||||
// setDrawGridLines(false)
|
|
||||||
// setDrawZeroLine(false)
|
|
||||||
setDrawAxisLine(false)
|
|
||||||
disableGridDashedLine()
|
|
||||||
invalidateOutline()
|
|
||||||
}
|
|
||||||
|
|
||||||
axisLeft.apply {
|
|
||||||
axisMinimum = 0f
|
|
||||||
isEnabled = false
|
|
||||||
axisLineColor = mainColor
|
|
||||||
this.textColor = textColor
|
|
||||||
setDrawZeroLine(false)
|
|
||||||
setLabelCount(0, true)
|
|
||||||
setDrawGridLines(false)
|
|
||||||
setDrawBorders(false)
|
|
||||||
}
|
|
||||||
axisRight.apply {
|
|
||||||
axisMinimum = 0f
|
|
||||||
this.textColor = textColor
|
|
||||||
setLabelCount(4, true)
|
|
||||||
}
|
|
||||||
setNoDataTextColor(textColor)
|
|
||||||
|
|
||||||
legend.isEnabled = false
|
|
||||||
isDragEnabled = true
|
|
||||||
// isScaleYEnabled = false
|
|
||||||
description = Description().apply { isEnabled = false }
|
|
||||||
isScaleXEnabled = true
|
|
||||||
setPinchZoom(false)
|
|
||||||
setDrawGridBackground(false)
|
|
||||||
setDrawBorders(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DateValueFormatter(
|
|
||||||
private val transformer: Int = 1
|
|
||||||
) : ValueFormatter() {
|
|
||||||
override fun getAxisLabel(value: Float, axis: AxisBase?): String {
|
|
||||||
return SimpleDateFormat(
|
|
||||||
"yyyy-MM-dd",
|
|
||||||
Locale.getDefault()
|
|
||||||
).format(Date(value.toLong() * transformer))
|
|
||||||
// return super.getAxisLabel(value, axis)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply Material theme to a DzeioChart [ChartView]
|
* Apply Material theme to a DzeioChart [ChartView]
|
||||||
*/
|
*/
|
||||||
fun materielTheme(chart: ChartView, view: View) {
|
fun materielTheme(chart: ChartView, view: View) {
|
||||||
|
|
||||||
val errorColor = MaterialColors.getColor(
|
|
||||||
view,
|
|
||||||
com.google.android.material.R.attr.colorError
|
|
||||||
)
|
|
||||||
|
|
||||||
chart.apply {
|
chart.apply {
|
||||||
|
annotator.apply {
|
||||||
|
backgroundPaint.color = MaterialColors.getColor(
|
||||||
|
view,
|
||||||
|
com.google.android.material.R.attr.colorBackgroundFloating
|
||||||
|
)
|
||||||
|
titlePaint.color = MaterialColors.getColor(
|
||||||
|
view,
|
||||||
|
com.google.android.material.R.attr.colorOnBackground
|
||||||
|
)
|
||||||
|
subTitlePaint.color = MaterialColors.getColor(
|
||||||
|
view,
|
||||||
|
com.google.android.material.R.attr.colorOnBackground
|
||||||
|
)
|
||||||
|
|
||||||
|
orientation = Annotation.Orientation.VERTICAL
|
||||||
|
|
||||||
|
annotationTitleFormat = { it.y.roundToLong().toString() }
|
||||||
|
annotationSubTitleFormat = annotationSubTitleFormat@{
|
||||||
|
val formatter = DateFormat.getDateTimeInstance(
|
||||||
|
DateFormat.SHORT,
|
||||||
|
DateFormat.SHORT,
|
||||||
|
Locale.getDefault()
|
||||||
|
)
|
||||||
|
return@annotationSubTitleFormat formatter.format(Date(it.x.roundToLong()))
|
||||||
|
}
|
||||||
|
}
|
||||||
yAxis.apply {
|
yAxis.apply {
|
||||||
textLabel.color = MaterialColors.getColor(
|
textLabel.color = MaterialColors.getColor(
|
||||||
view,
|
view,
|
||||||
@ -122,7 +56,10 @@ object ChartUtils {
|
|||||||
view,
|
view,
|
||||||
com.google.android.material.R.attr.colorOnPrimaryContainer
|
com.google.android.material.R.attr.colorOnPrimaryContainer
|
||||||
)
|
)
|
||||||
goalLinePaint.color = errorColor
|
goalLinePaint.color = MaterialColors.getColor(
|
||||||
|
view,
|
||||||
|
com.google.android.material.R.attr.colorError
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
xAxis.apply {
|
xAxis.apply {
|
||||||
|
@ -155,7 +155,14 @@ class Configuration(
|
|||||||
private val defaultValue: Float = -1f
|
private val defaultValue: Float = -1f
|
||||||
) : Field<Float?>(defaultValue) {
|
) : Field<Float?>(defaultValue) {
|
||||||
override fun exists(): Boolean = prefs.contains(key)
|
override fun exists(): Boolean = prefs.contains(key)
|
||||||
override fun internalGet(): Float = prefs.getFloat(key, defaultValue)
|
override fun internalGet(): Float {
|
||||||
|
return try {
|
||||||
|
prefs.getFloat(key, defaultValue)
|
||||||
|
} catch (e: ClassCastException) {
|
||||||
|
val it = prefs.getString(key, "")
|
||||||
|
it?.toFloatOrNull() ?: defaultValue
|
||||||
|
}
|
||||||
|
}
|
||||||
override fun internalSet(value: Float?) =
|
override fun internalSet(value: Float?) =
|
||||||
prefs.edit { if (value == null) remove(key) else putFloat(key, value) }
|
prefs.edit { if (value == null) remove(key) else putFloat(key, value) }
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import android.app.ActivityManager
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.dzeio.openhealth.ui.MainActivity
|
import com.dzeio.openhealth.Application
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utils class for services
|
* Utils class for services
|
||||||
@ -20,11 +20,11 @@ object ServiceUtils {
|
|||||||
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||||
for (runninService in activityManager.getRunningServices(Integer.MAX_VALUE)) {
|
for (runninService in activityManager.getRunningServices(Integer.MAX_VALUE)) {
|
||||||
if (service.name.equals(runninService.service.className)) {
|
if (service.name.equals(runninService.service.className)) {
|
||||||
Log.w(MainActivity.TAG, "Service already existing, not starting again")
|
Log.w(Application.TAG, "Service already existing, not starting again")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log.i(MainActivity.TAG, "Starting service ${service.name}")
|
Log.i(Application.TAG, "Starting service ${service.name}")
|
||||||
Intent(context, service).also { intent -> context.startService(intent) }
|
Intent(context, service).also { intent -> context.startService(intent) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,7 @@
|
|||||||
android:id="@+id/bottom_nav"
|
android:id="@+id/bottom_nav"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:fitsSystemWindows="false"
|
android:fitsSystemWindows="false"
|
||||||
|
app:labelVisibilityMode="labeled"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
@ -303,10 +303,11 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<com.github.mikephil.charting.charts.LineChart
|
<com.dzeio.charts.ChartView
|
||||||
android:id="@+id/weight_graph"
|
android:id="@+id/weight_graph"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="200dp"
|
android:layout_height="200dp"
|
||||||
|
android:layout_margin="8dp"
|
||||||
android:minHeight="200dp" />
|
android:minHeight="200dp" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/nav_extensions"
|
android:id="@+id/nav_extensions"
|
||||||
android:enabled="false"
|
|
||||||
android:title="@string/menu_extensions"
|
android:title="@string/menu_extensions"
|
||||||
android:icon="@drawable/ic_outline_account_circle_24"/>
|
android:icon="@drawable/ic_outline_account_circle_24"/>
|
||||||
</menu>
|
</menu>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user