1
0
mirror of https://github.com/dzeiocom/OpenHealth.git synced 2025-04-25 12:22:15 +00:00

feat: updated

This commit is contained in:
Florian Bouillon 2023-01-19 17:20:14 +01:00
parent 7875271b7e
commit 357024770a
13 changed files with 110 additions and 379 deletions

View File

@ -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")

View File

@ -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()
}
}
}

View File

@ -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)
} }
} }
} }

View File

@ -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()

View File

@ -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()
} }
} }

View File

@ -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()))
} }
} }
} }

View File

@ -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
}
}

View File

@ -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 {

View File

@ -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) }
} }

View File

@ -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) }
} }
} }

View File

@ -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"

View File

@ -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>

View File

@ -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>