diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 24e0003..24afa90 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -58,7 +58,7 @@ android { targetSdk = sdkTarget // Semantic Versioning - versionName = "1.0.0" + versionName = "0.1.0" versionCode = 1 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" @@ -82,6 +82,7 @@ android { getByName("release") { // Slimmer version isMinifyEnabled = true + isDebuggable = true proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") signingConfig = signingConfigs.getByName("release") } @@ -129,7 +130,7 @@ dependencies { implementation("androidx.core:core-ktx:1.9.0") implementation("androidx.appcompat:appcompat:1.7.0-alpha01") implementation("javax.inject:javax.inject:1") - implementation("com.google.android.material:material:1.8.0-alpha03") + implementation("com.google.android.material:material:1.8.0-beta01") implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.5.1") implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1") @@ -173,13 +174,13 @@ dependencies { kapt("com.google.dagger:hilt-compiler:2.43.2") // Google Fit - implementation("com.google.android.gms:play-services-fitness:21.1.0") - implementation("com.google.android.gms:play-services-auth:20.4.0") - implementation("androidx.health.connect:connect-client:1.0.0-alpha07") +// implementation("com.google.android.gms:play-services-fitness:21.1.0") +// implementation("com.google.android.gms:play-services-auth:20.4.0") +// implementation("androidx.health.connect:connect-client:1.0.0-alpha08") // Samsung Health - implementation(files("libs/samsung-health-data-1.5.0.aar")) - implementation("com.google.code.gson:gson:2.9.1") +// implementation(files("libs/samsung-health-data-1.5.0.aar")) +// implementation("com.google.code.gson:gson:2.9.1") // ROOM implementation("androidx.room:room-runtime:2.4.3") @@ -195,6 +196,7 @@ dependencies { // OSS Licenses implementation("com.google.android.gms:play-services-oss-licenses:17.0.0") + // Open Food Fact implementation("com.squareup.retrofit2:retrofit:2.9.0") implementation("com.squareup.retrofit2:converter-gson:2.9.0") implementation("com.squareup.okhttp3:logging-interceptor:4.9.1") diff --git a/app/src/main/java/com/dzeio/openhealth/Settings.kt b/app/src/main/java/com/dzeio/openhealth/Settings.kt index a5128e7..16ccca4 100644 --- a/app/src/main/java/com/dzeio/openhealth/Settings.kt +++ b/app/src/main/java/com/dzeio/openhealth/Settings.kt @@ -26,4 +26,9 @@ object Settings { * format in which the weight the user want it to be displayed as */ const val MASS_UNIT = "com.dzeio.open-health.unit.mass" + + /** + * Goal number of steps each days + */ + const val STEPS_GOAL = "com.dzeio.open-health.steps.goal-daily" } diff --git a/app/src/main/java/com/dzeio/openhealth/adapters/StepsAdapter.kt b/app/src/main/java/com/dzeio/openhealth/adapters/StepsAdapter.kt index 1560fd3..4583da7 100644 --- a/app/src/main/java/com/dzeio/openhealth/adapters/StepsAdapter.kt +++ b/app/src/main/java/com/dzeio/openhealth/adapters/StepsAdapter.kt @@ -19,7 +19,7 @@ class StepsAdapter() : BaseAdapter() { item: Step, position: Int ) { - holder.binding.value.text = "${item.value}steps" + holder.binding.value.text = "${item.value} steps" holder.binding.datetime.text = item.formatTimestamp() holder.binding.edit.setOnClickListener { onItemClick?.invoke(item) diff --git a/app/src/main/java/com/dzeio/openhealth/data/openfoodfact/OFFProduct.kt b/app/src/main/java/com/dzeio/openhealth/data/openfoodfact/OFFProduct.kt index 02e4c59..c6a253f 100644 --- a/app/src/main/java/com/dzeio/openhealth/data/openfoodfact/OFFProduct.kt +++ b/app/src/main/java/com/dzeio/openhealth/data/openfoodfact/OFFProduct.kt @@ -8,7 +8,7 @@ data class OFFProduct( @SerializedName("product_name") var name: String, - @SerializedName("serving_quantity") + @SerializedName("serving_size") var serving: String, @SerializedName("nutriments") diff --git a/app/src/main/java/com/dzeio/openhealth/data/step/StepDao.kt b/app/src/main/java/com/dzeio/openhealth/data/step/StepDao.kt index ecdab8d..467f8a9 100644 --- a/app/src/main/java/com/dzeio/openhealth/data/step/StepDao.kt +++ b/app/src/main/java/com/dzeio/openhealth/data/step/StepDao.kt @@ -11,13 +11,16 @@ interface StepDao : BaseDao { @Query("SELECT * FROM Step ORDER BY timestamp DESC") fun getAll(): Flow> - @Query("SELECT * FROM Step where id = :weightId") + @Query("SELECT * FROM Step WHERE timestamp >= :time") + fun getAfter(time: Long): Flow> + + @Query("SELECT * FROM Step WHERE id = :weightId") fun getOne(weightId: Long): Flow - @Query("Select count(*) from Step") + @Query("SELECT count(*) FROM Step") fun getCount(): Flow - @Query("Select * FROM Step ORDER BY timestamp DESC LIMIT 1") + @Query("SELECT * FROM Step ORDER BY timestamp DESC LIMIT 1") fun last(): Flow @Query("DELETE FROM Step where source = :source") diff --git a/app/src/main/java/com/dzeio/openhealth/data/step/StepRepository.kt b/app/src/main/java/com/dzeio/openhealth/data/step/StepRepository.kt index aca13bd..837a560 100644 --- a/app/src/main/java/com/dzeio/openhealth/data/step/StepRepository.kt +++ b/app/src/main/java/com/dzeio/openhealth/data/step/StepRepository.kt @@ -2,7 +2,8 @@ package com.dzeio.openhealth.data.step import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.withTimeoutOrNull +import java.util.Calendar +import java.util.TimeZone import javax.inject.Inject import javax.inject.Singleton @@ -23,7 +24,13 @@ class StepRepository @Inject constructor( suspend fun deleteFromSource(value: String) = stepDao.deleteFromSource(value) suspend fun todaySteps(): Int { - val steps = getSteps().firstOrNull() + val cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")) + + cal.set(Calendar.HOUR, 0) + cal.set(Calendar.MINUTE, 0) + cal.set(Calendar.SECOND, 0) + cal.set(Calendar.MILLISECOND, 0) + val steps = stepDao.getAfter(cal.timeInMillis).firstOrNull() if (steps == null) { return 0 } diff --git a/app/src/main/java/com/dzeio/openhealth/ui/about/AboutFragment.kt b/app/src/main/java/com/dzeio/openhealth/ui/about/AboutFragment.kt index 63f67b6..8726f0b 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/about/AboutFragment.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/about/AboutFragment.kt @@ -27,7 +27,7 @@ class AboutFragment : BaseStaticFragment() { resources.getString(R.string.version_number, BuildConfig.VERSION_NAME) binding.contactUs.setOnClickListener { - openLink("mailto:context.openhealth@dze.io") + openLink("mailto:contact.openhealth@dze.io") } binding.github.setOnClickListener { diff --git a/app/src/main/java/com/dzeio/openhealth/ui/browse/BrowseFragment.kt b/app/src/main/java/com/dzeio/openhealth/ui/browse/BrowseFragment.kt index 2fe51ee..8a4782d 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/browse/BrowseFragment.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/browse/BrowseFragment.kt @@ -92,18 +92,29 @@ class BrowseFragment : } viewModel.steps.observe(viewLifecycleOwner) { - binding.stepsText.setText("$it of xxx steps") + updateStepsText(it, viewModel.stepsGoal.value) + } + + viewModel.stepsGoal.observe(viewLifecycleOwner) { + updateStepsText(viewModel.steps.value, it) } viewModel.weight.observe(viewLifecycleOwner) { binding.weightText.setText( String.format( resources.getString(R.string.weight_current), - it, - resources.getString(R.string.unit_mass_kilogram_unit) + String.format(resources.getString(R.string.unit_mass_kilogram_unit), it) ) ) } } + + private fun updateStepsText(numberOfSteps: Int?, goal: Int?) { + var text = "${numberOfSteps ?: 0} steps" + if (goal != null) { + text = "${numberOfSteps ?: 0} of $goal steps" + } + binding.stepsText.setText(text) + } } diff --git a/app/src/main/java/com/dzeio/openhealth/ui/browse/BrowseViewModel.kt b/app/src/main/java/com/dzeio/openhealth/ui/browse/BrowseViewModel.kt index 9a3d215..8b8eab9 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/browse/BrowseViewModel.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/browse/BrowseViewModel.kt @@ -3,9 +3,11 @@ package com.dzeio.openhealth.ui.browse import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope +import com.dzeio.openhealth.Settings import com.dzeio.openhealth.core.BaseViewModel import com.dzeio.openhealth.data.step.StepRepository import com.dzeio.openhealth.data.weight.WeightRepository +import com.dzeio.openhealth.utils.Configuration import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch @@ -14,12 +16,16 @@ import javax.inject.Inject @HiltViewModel class BrowseViewModel @Inject internal constructor( stepRepository: StepRepository, - weightRepository: WeightRepository + weightRepository: WeightRepository, + private val config: Configuration ) : BaseViewModel() { private val _steps = MutableLiveData(0) val steps: LiveData = _steps + private val _stepsGoal: MutableLiveData = MutableLiveData() + val stepsGoal: LiveData = _stepsGoal + private val _weight = MutableLiveData(0f) val weight: LiveData = _weight @@ -36,5 +42,9 @@ class BrowseViewModel @Inject internal constructor( _weight.postValue(it.weight) } } + + this._stepsGoal.postValue( + config.getInt(Settings.STEPS_GOAL).value + ) } } diff --git a/app/src/main/java/com/dzeio/openhealth/ui/settings/SettingsFragment.kt b/app/src/main/java/com/dzeio/openhealth/ui/settings/SettingsFragment.kt index 856dfad..fe8a7dc 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/settings/SettingsFragment.kt @@ -91,5 +91,10 @@ class SettingsFragment : PreferenceFragmentCompat() { return@setOnPreferenceChangeListener true } + + val stepsGoalPreference = findPreference(Settings.STEPS_GOAL) + stepsGoalPreference.apply { + + } } } diff --git a/app/src/main/java/com/dzeio/openhealth/ui/steps/StepsHomeFragment.kt b/app/src/main/java/com/dzeio/openhealth/ui/steps/StepsHomeFragment.kt index 19fcbe2..9289d8d 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/steps/StepsHomeFragment.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/steps/StepsHomeFragment.kt @@ -64,12 +64,16 @@ class StepsHomeFragment : ) } + val errorColor = MaterialColors.getColor( + requireView(), + com.google.android.material.R.attr.colorError + ) + chart.apply { series = arrayListOf(serie) // debug = true yAxis.apply { - setYMax(500f) textLabel.color = MaterialColors.getColor( requireView(), com.google.android.material.R.attr.colorOnPrimaryContainer @@ -78,14 +82,15 @@ class StepsHomeFragment : requireView(), com.google.android.material.R.attr.colorOnPrimaryContainer ) + goalLinePaint.color = errorColor // onValueFormat = { value -> "${value.toInt()}" } } xAxis.apply { - increment = 3600000.0 + increment = 86400000.0 // displayCount = 168 - displayCount = 10 +// displayCount = 10 textPaint.color = MaterialColors.getColor( requireView(), com.google.android.material.R.attr.colorOnPrimaryContainer @@ -103,6 +108,11 @@ class StepsHomeFragment : } } + viewModel.goal.observe(viewLifecycleOwner) { + chart.yAxis.setGoalLine(it?.toFloat()) + chart.refresh() + } + viewModel.items.observe(viewLifecycleOwner) { list -> adapter.set(list) @@ -110,12 +120,7 @@ class StepsHomeFragment : return@observe } - val cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")) - cal.set(Calendar.HOUR, 0) - cal.set(Calendar.MINUTE, 0) - cal.set(Calendar.SECOND, 0) - cal.set(Calendar.MILLISECOND, 0) // chart.animation.enabled = false // chart.animation.refreshRate = 60 @@ -125,11 +130,33 @@ class StepsHomeFragment : // chart.xAxis.labels.size = 32f - serie.entries = list.reversed().map { - return@map Entry(it.timestamp.toDouble(), it.value.toFloat()) - } as ArrayList + val entries: HashMap = HashMap() - chart.xAxis.x = serie.entries.first().x + list.forEach { + val cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")) + cal.timeInMillis = it.timestamp + + cal.set(Calendar.HOUR, 0) + cal.set(Calendar.AM_PM, Calendar.AM) + val ts = cal.timeInMillis + if (!entries.containsKey(ts)) { + entries[ts] = Entry((ts).toDouble(), 0F, errorColor) + } + + entries[ts]!!.y += it.value.toFloat() + + if (viewModel.goal.value != null) { + if (entries[ts]!!.y > viewModel.goal.value!!) { + entries[ts]!!.color = null + } + } else { + entries[ts]!!.color = null + } + } + + serie.entries = ArrayList(entries.values) + + chart.xAxis.x = chart.xAxis.getXMax() chart.refresh() diff --git a/app/src/main/java/com/dzeio/openhealth/ui/steps/StepsHomeViewModel.kt b/app/src/main/java/com/dzeio/openhealth/ui/steps/StepsHomeViewModel.kt index 3908ac8..cec1ed5 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/steps/StepsHomeViewModel.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/steps/StepsHomeViewModel.kt @@ -1,10 +1,13 @@ package com.dzeio.openhealth.ui.steps +import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope +import com.dzeio.openhealth.Settings import com.dzeio.openhealth.core.BaseViewModel import com.dzeio.openhealth.data.step.Step import com.dzeio.openhealth.data.step.StepRepository +import com.dzeio.openhealth.utils.Configuration import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch @@ -12,15 +15,23 @@ import javax.inject.Inject @HiltViewModel class StepsHomeViewModel@Inject internal constructor( - private val stepRepository: StepRepository + private val stepRepository: StepRepository, + private val config: Configuration ) : BaseViewModel() { val items: MutableLiveData> = MutableLiveData() + private val _goal: MutableLiveData = MutableLiveData() + val goal: LiveData = _goal + fun init() { viewModelScope.launch { stepRepository.getSteps().collectLatest { items.postValue(it) } } + + this._goal.postValue( + config.getInt(Settings.STEPS_GOAL).value + ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/dzeio/openhealth/utils/fields/IntEditTextPreference.kt b/app/src/main/java/com/dzeio/openhealth/utils/fields/IntEditTextPreference.kt new file mode 100644 index 0000000..e203d2d --- /dev/null +++ b/app/src/main/java/com/dzeio/openhealth/utils/fields/IntEditTextPreference.kt @@ -0,0 +1,87 @@ +package com.dzeio.openhealth.utils.fields + +import android.content.Context +import android.text.InputType +import android.text.TextUtils +import android.util.AttributeSet +import android.util.Log +import android.widget.EditText +import androidx.preference.EditTextPreference + + +class IntEditTextPreference : EditTextPreference, EditTextPreference.OnBindEditTextListener { + + private var txt: String? = null + + constructor( + context: Context, + attrs: AttributeSet?, + defStyleAttr: Int, + defStyleRes: Int + ) : super(context, attrs, defStyleAttr, defStyleRes) { + setOnBindEditTextListener(this) + } + + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super( + context, + attrs, + defStyleAttr + ) { + setOnBindEditTextListener(this) + } + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { + setOnBindEditTextListener(this) + } + + constructor(context: Context) : super(context) { + setOnBindEditTextListener(this) + } + + /** + * Saves the text to the current data storage. + * + * @param text The text to save + */ + override fun setText(text: String?) { + + val wasBlocking = shouldDisableDependents() + val pouet = Integer.parseInt(text.toString()) + this.txt = text + pouet::class.simpleName?.let { Log.d("pouet2", it) } + persistInt(pouet) + val isBlocking = shouldDisableDependents() + if (isBlocking != wasBlocking) { + notifyDependencyChange(isBlocking) + } + notifyChanged() + } + + override fun getText(): String? { + return this.txt + } + + override fun onSetInitialValue(defaultValue: Any?) { + var value: Int + if (defaultValue != null) { + val strDefaultValue = defaultValue as String + val defaultIntValue = strDefaultValue.toInt() + value = getPersistedInt(defaultIntValue) + } else { + try { + value = getPersistedInt(0) + } catch (e: ClassCastException) { + value = 0 + } + } + text = value.toString() + } + + override fun shouldDisableDependents(): Boolean { + return TextUtils.isEmpty(text) || super.shouldDisableDependents() + } + + override fun onBindEditText(editText: EditText) { + editText.inputType = InputType.TYPE_CLASS_NUMBER + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 04a11bb..0dc6b80 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,11 +13,11 @@ Kilogram Kilograms - kg + %1$skg Pound Pounds - lbs + %1$slbs Milliliter Milliliters @@ -52,7 +52,7 @@ You declined a permission, you can\'t use this extension unless you enable it manually Modifiy daily goal Steps - Current weight: %1$s%2$s + Current weight: %1$s diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index a9fa6fe..16defc6 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -13,6 +13,7 @@ android:key="com.dzeio.open-health.app.language" android:title="@string/languages" /> + - - + + + + + diff --git a/charts/src/main/java/com/dzeio/charts/Entry.kt b/charts/src/main/java/com/dzeio/charts/Entry.kt index 13c74a2..526fec8 100644 --- a/charts/src/main/java/com/dzeio/charts/Entry.kt +++ b/charts/src/main/java/com/dzeio/charts/Entry.kt @@ -4,6 +4,7 @@ package com.dzeio.charts * A Base entry for any charts */ data class Entry( - val x: Double, - val y: Float + var x: Double, + var y: Float, + var color: Int? = null ) diff --git a/charts/src/main/java/com/dzeio/charts/axis/XAxis.kt b/charts/src/main/java/com/dzeio/charts/axis/XAxis.kt index 7fad8c9..dc3cecd 100644 --- a/charts/src/main/java/com/dzeio/charts/axis/XAxis.kt +++ b/charts/src/main/java/com/dzeio/charts/axis/XAxis.kt @@ -14,12 +14,12 @@ class XAxis( override var x: Double = 0.0 set(value) { val max = getXMax() - increment * displayCount - if (value > max) { + val min = getXMin() + if (value > max && min <= max) { field = max return } - val min = getXMin() if (value < min) { field = min return @@ -103,4 +103,4 @@ class XAxis( override fun refresh() { // TODO("Not yet implemented") } -} \ No newline at end of file +} diff --git a/charts/src/main/java/com/dzeio/charts/axis/YAxis.kt b/charts/src/main/java/com/dzeio/charts/axis/YAxis.kt index 618892b..314520b 100644 --- a/charts/src/main/java/com/dzeio/charts/axis/YAxis.kt +++ b/charts/src/main/java/com/dzeio/charts/axis/YAxis.kt @@ -6,6 +6,7 @@ import android.graphics.Paint import android.graphics.Rect import android.graphics.RectF import com.dzeio.charts.ChartViewInterface +import com.dzeio.charts.utils.drawDottedLine class YAxis( private val view: ChartViewInterface @@ -25,6 +26,12 @@ class YAxis( color = Color.BLUE } + override val goalLinePaint = Paint().apply { + isAntiAlias = true + color = Color.RED + strokeWidth = 4f + } + var onValueFormat: (value: Float) -> String = { it -> it.toString() } override var labelCount = 5 @@ -49,15 +56,19 @@ class YAxis( return max!! } if (view.series.isEmpty()) { - return 100f + return (this.goalLine ?: 90f) + 10f } - return view.series + val seriesMax = view.series .maxOf { serie -> if (serie.getDisplayedEntries().isEmpty()) { return@maxOf 0f } return@maxOf serie.getDisplayedEntries().maxOf { entry -> entry.y } } + if (this.goalLine != null) { + return if (seriesMax > this.goalLine!!) seriesMax else this.goalLine!! + 1000f + } + return seriesMax } override fun getYMin(): Float { @@ -65,7 +76,7 @@ class YAxis( return min!! } if (view.series.isEmpty()) { - return 0f + return this.goalLine ?: 0f } return view.series .minOf { serie -> @@ -80,6 +91,7 @@ class YAxis( if (!enabled) { return 0f } + val min = getYMin() val max = getYMax() - min val top = space.top @@ -89,7 +101,7 @@ class YAxis( val increment = (bottom - top) / labelCount val valueIncrement = (max - min) / labelCount for (index in 0 until labelCount) { - val text = onValueFormat((valueIncrement * (index + 1))).toString() + val text = onValueFormat((valueIncrement * (index + 1))) textLabel.getTextBounds(text, 0, text.length, rect) maxWidth = maxWidth.coerceAtLeast(rect.width().toFloat()) @@ -105,10 +117,29 @@ class YAxis( canvas.drawLine(space.left, posY, space.right - maxWidth - 32f, posY, linePaint) } + + if (this.goalLine != null) { + val pos = (1 - this.goalLine!! / max) * space.height() + space.top + canvas.drawDottedLine( + 0f, + pos, + space.right - maxWidth - 32f, + pos, + space.right / 20, + goalLinePaint + ) + } + return maxWidth + 32f } override fun refresh() { // TODO("Not yet implemented") } + + private var goalLine: Float? = null + + override fun setGoalLine(height: Float?) { + goalLine = height + } } diff --git a/charts/src/main/java/com/dzeio/charts/axis/YAxisInterface.kt b/charts/src/main/java/com/dzeio/charts/axis/YAxisInterface.kt index 172b9c4..b82afe2 100644 --- a/charts/src/main/java/com/dzeio/charts/axis/YAxisInterface.kt +++ b/charts/src/main/java/com/dzeio/charts/axis/YAxisInterface.kt @@ -28,6 +28,11 @@ sealed interface YAxisInterface { */ val linePaint: Paint + /** + * Goal line paint + */ + val goalLinePaint: Paint + /** * run when manually refreshing the system * @@ -72,4 +77,9 @@ sealed interface YAxisInterface { * @return the width of the sidebar */ fun onDraw(canvas: Canvas, space: RectF): Float -} \ No newline at end of file + + /** + * Add a Goal line + */ + fun setGoalLine(height: Float?) +} diff --git a/charts/src/main/java/com/dzeio/charts/series/BarSerie.kt b/charts/src/main/java/com/dzeio/charts/series/BarSerie.kt index 9f35996..e4e4813 100644 --- a/charts/src/main/java/com/dzeio/charts/series/BarSerie.kt +++ b/charts/src/main/java/com/dzeio/charts/series/BarSerie.kt @@ -5,7 +5,6 @@ import android.graphics.Color import android.graphics.Paint import android.graphics.Rect import android.graphics.RectF -import android.util.Log import com.dzeio.charts.ChartView import com.dzeio.charts.utils.drawRoundRect @@ -47,18 +46,21 @@ class BarSerie( for (entry in displayedEntries) { // calculated height in percent from 0 to 100 val top = (1 - entry.y / max) * drawableSpace.height() + drawableSpace.top - var posX = drawableSpace.left + (view.xAxis.getPositionOnRect(entry, drawableSpace) - view.xAxis.getXOffset(drawableSpace)).toFloat() + var posX = drawableSpace.left + (view.xAxis.getPositionOnRect( + entry, + drawableSpace + ) - view.xAxis.getXOffset(drawableSpace)).toFloat() // Log.d(TAG, "gpor = ${view.xAxis.getPositionOnRect(entry, space)}, gxo = ${view.xAxis.getXOffset(space)}") // Log.d(TAG, "max = $max, y = ${entry.y}, height = $height") // Log.d(TAG, "posX: ${posX / 60 / 60 / 1000}, offsetX = ${view.xAxis.x / (1000 * 60 * 60)}, x = ${entry.x / (1000 * 60 * 60)}, pouet: ${(view.xAxis.x + view.xAxis.displayCount * view.xAxis.increment) / (1000 * 60 * 60)}") - Log.d( - TAG, """ - ${posX}, - $top, - ${(posX + barWidth)}, - ${drawableSpace.bottom}""".trimIndent() - ) +// Log.d( +// TAG, """ +// ${posX}, +// $top, +// ${(posX + barWidth)}, +// ${drawableSpace.bottom}""".trimIndent() +// ) val right = (posX + barWidth).coerceAtMost(drawableSpace.right) @@ -72,6 +74,13 @@ class BarSerie( continue } + // handle color recoloration + val paint = Paint(barPaint) + + if (entry.color != null) { + paint.color = entry.color!! + } + canvas.drawRoundRect( posX, top, @@ -82,7 +91,7 @@ class BarSerie( 32f, 0f, 0f, - barPaint + paint ) // handle text display diff --git a/charts/src/main/java/com/dzeio/charts/utils/CanvasUtils.kt b/charts/src/main/java/com/dzeio/charts/utils/CanvasUtils.kt index 4f053fd..781e76a 100644 --- a/charts/src/main/java/com/dzeio/charts/utils/CanvasUtils.kt +++ b/charts/src/main/java/com/dzeio/charts/utils/CanvasUtils.kt @@ -55,7 +55,17 @@ fun Canvas.drawDottedLine( /** * A more customizable drawRoundRect function */ -fun Canvas.drawRoundRect(left: Float, top: Float, right: Float, bottom: Float, topLeft: Float, topRight: Float, bottomLeft: Float, bottomRight: Float, paint: Paint) { +fun Canvas.drawRoundRect( + left: Float, + top: Float, + right: Float, + bottom: Float, + topLeft: Float, + topRight: Float, + bottomLeft: Float, + bottomRight: Float, + paint: Paint +) { val maxRound = arrayOf(topLeft, topRight, bottomLeft, bottomRight).maxOf { it } val width = right - left val height = bottom - top @@ -81,14 +91,30 @@ fun Canvas.drawRoundRect(left: Float, top: Float, right: Float, bottom: Float, t if (bottomLeft == 0f) { drawRect(left, bottom - height / 2, left + width / 2, bottom, paint) } else { - drawRoundRect(left, bottom - height / 2, left + width / 2, bottom, bottomLeft, bottomLeft, paint) + drawRoundRect( + left, + bottom - height / 2, + left + width / 2, + bottom, + bottomLeft, + bottomLeft, + paint + ) } // bottom right border if (bottomRight == 0f) { drawRect(right - width / 2, bottom - height / 2, right, bottom, paint) } else { - drawRoundRect(right - width / 2, bottom - height / 2, right, bottom, bottomRight, bottomRight, paint) + drawRoundRect( + right - width / 2, + bottom - height / 2, + right, + bottom, + bottomRight, + bottomRight, + paint + ) } } @@ -96,6 +122,23 @@ fun Canvas.drawRoundRect(left: Float, top: Float, right: Float, bottom: Float, t /** * A more customizable drawRoundRect function */ -fun Canvas.drawRoundRect(rect: RectF, topLeft: Float, topRight: Float, bottomLeft: Float, bottomRight: Float, paint: Paint) { - drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, topLeft, topRight, bottomLeft, bottomRight, paint) -} \ No newline at end of file +fun Canvas.drawRoundRect( + rect: RectF, + topLeft: Float, + topRight: Float, + bottomLeft: Float, + bottomRight: Float, + paint: Paint +) { + drawRoundRect( + rect.left, + rect.top, + rect.right, + rect.bottom, + topLeft, + topRight, + bottomLeft, + bottomRight, + paint + ) +}