From 9ebb85dbae0221a50bd3cadefd133cb412be35c3 Mon Sep 17 00:00:00 2001 From: Prince Raj Date: Fri, 22 Dec 2023 13:19:37 +0530 Subject: [PATCH] feat: add Pie and Combined(bar + line) chart in HealthFragment - Add menu(in toolbar) in HealthFragment for changing chart type - Add Pie chart for KCal, Time and Distance in HealthFragment - Add Combined chart for KCal, Time and Distance in HealthFragment --- .../ui/adapter/CombinedChartAdapter.kt | 24 +++ .../{ViewAdapter.kt => LineChartAdapter.kt} | 2 +- .../cyclofit/ui/adapter/PieChartAdapter.kt | 24 +++ .../fragment/DistanceCombineChartFragment.kt | 158 +++++++++++++++ .../ui/fragment/DistancePieChartFragment.kt | 124 ++++++++++++ .../cyclofit/ui/fragment/HealthFragment.kt | 91 ++++++--- .../ui/fragment/KcalCombinedChartFragment.kt | 183 ++++++++++++++++++ .../cyclofit/ui/fragment/KcalFragment.kt | 117 ++++++----- .../ui/fragment/KcalPeiChartFragment.kt | 148 ++++++++++++++ .../ui/fragment/TimeCombinedChartFragment.kt | 156 +++++++++++++++ .../ui/fragment/TimePieCharFragment.kt | 123 ++++++++++++ .../example/cyclofit/ui/utils/ChartUtils.kt | 20 ++ .../fragment_distance_combine_chart.xml | 12 ++ .../layout/fragment_distance_pie_chart.xml | 12 ++ app/src/main/res/layout/fragment_health.xml | 148 +++++++------- .../layout/fragment_kcal_combined_chart.xml | 12 ++ .../res/layout/fragment_kcal_pei_chart.xml | 12 ++ .../layout/fragment_time_combined_chart.xml | 12 ++ .../res/layout/fragment_time_pie_char.xml | 12 ++ app/src/main/res/menu/health_top.xml | 12 ++ 20 files changed, 1246 insertions(+), 156 deletions(-) create mode 100644 app/src/main/java/com/example/cyclofit/ui/adapter/CombinedChartAdapter.kt rename app/src/main/java/com/example/cyclofit/ui/adapter/{ViewAdapter.kt => LineChartAdapter.kt} (83%) create mode 100644 app/src/main/java/com/example/cyclofit/ui/adapter/PieChartAdapter.kt create mode 100644 app/src/main/java/com/example/cyclofit/ui/fragment/DistanceCombineChartFragment.kt create mode 100644 app/src/main/java/com/example/cyclofit/ui/fragment/DistancePieChartFragment.kt create mode 100644 app/src/main/java/com/example/cyclofit/ui/fragment/KcalCombinedChartFragment.kt create mode 100644 app/src/main/java/com/example/cyclofit/ui/fragment/KcalPeiChartFragment.kt create mode 100644 app/src/main/java/com/example/cyclofit/ui/fragment/TimeCombinedChartFragment.kt create mode 100644 app/src/main/java/com/example/cyclofit/ui/fragment/TimePieCharFragment.kt create mode 100644 app/src/main/java/com/example/cyclofit/ui/utils/ChartUtils.kt create mode 100644 app/src/main/res/layout/fragment_distance_combine_chart.xml create mode 100644 app/src/main/res/layout/fragment_distance_pie_chart.xml create mode 100644 app/src/main/res/layout/fragment_kcal_combined_chart.xml create mode 100644 app/src/main/res/layout/fragment_kcal_pei_chart.xml create mode 100644 app/src/main/res/layout/fragment_time_combined_chart.xml create mode 100644 app/src/main/res/layout/fragment_time_pie_char.xml create mode 100644 app/src/main/res/menu/health_top.xml diff --git a/app/src/main/java/com/example/cyclofit/ui/adapter/CombinedChartAdapter.kt b/app/src/main/java/com/example/cyclofit/ui/adapter/CombinedChartAdapter.kt new file mode 100644 index 0000000..b933ea4 --- /dev/null +++ b/app/src/main/java/com/example/cyclofit/ui/adapter/CombinedChartAdapter.kt @@ -0,0 +1,24 @@ +package com.example.cyclofit.ui.adapter + +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.lifecycle.Lifecycle +import androidx.viewpager2.adapter.FragmentStateAdapter +import com.example.cyclofit.ui.fragment.DistanceCombineChartFragment +import com.example.cyclofit.ui.fragment.KcalCombinedChartFragment +import com.example.cyclofit.ui.fragment.TimeCombinedChartFragment + +class CombinedChartAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) : + FragmentStateAdapter(fragmentManager, lifecycle) { + override fun getItemCount(): Int { + return 3 + } + + override fun createFragment(position: Int): Fragment { + return when (position) { + 0 -> KcalCombinedChartFragment() + 1 -> TimeCombinedChartFragment() + else -> DistanceCombineChartFragment() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/cyclofit/ui/adapter/ViewAdapter.kt b/app/src/main/java/com/example/cyclofit/ui/adapter/LineChartAdapter.kt similarity index 83% rename from app/src/main/java/com/example/cyclofit/ui/adapter/ViewAdapter.kt rename to app/src/main/java/com/example/cyclofit/ui/adapter/LineChartAdapter.kt index f3f63bd..a769508 100644 --- a/app/src/main/java/com/example/cyclofit/ui/adapter/ViewAdapter.kt +++ b/app/src/main/java/com/example/cyclofit/ui/adapter/LineChartAdapter.kt @@ -8,7 +8,7 @@ import com.example.cyclofit.ui.fragment.DistanceFragment import com.example.cyclofit.ui.fragment.KcalFragment import com.example.cyclofit.ui.fragment.TimeFragment -class ViewAdapter(fragmentManager: FragmentManager,lifecycle : Lifecycle) : FragmentStateAdapter(fragmentManager,lifecycle) { +class LineChartAdapter(fragmentManager: FragmentManager, lifecycle : Lifecycle) : FragmentStateAdapter(fragmentManager,lifecycle) { override fun getItemCount(): Int { return 3 } diff --git a/app/src/main/java/com/example/cyclofit/ui/adapter/PieChartAdapter.kt b/app/src/main/java/com/example/cyclofit/ui/adapter/PieChartAdapter.kt new file mode 100644 index 0000000..7f3bbb9 --- /dev/null +++ b/app/src/main/java/com/example/cyclofit/ui/adapter/PieChartAdapter.kt @@ -0,0 +1,24 @@ +package com.example.cyclofit.ui.adapter + +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.lifecycle.Lifecycle +import androidx.viewpager2.adapter.FragmentStateAdapter +import com.example.cyclofit.ui.fragment.DistancePieChartFragment +import com.example.cyclofit.ui.fragment.KcalPeiChartFragment +import com.example.cyclofit.ui.fragment.TimePieCharFragment + +class PieChartAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) : + FragmentStateAdapter(fragmentManager, lifecycle) { + override fun getItemCount(): Int { + return 3 + } + + override fun createFragment(position: Int): Fragment { + return when (position) { + 0 -> KcalPeiChartFragment() + 1 -> TimePieCharFragment() + else -> DistancePieChartFragment() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/cyclofit/ui/fragment/DistanceCombineChartFragment.kt b/app/src/main/java/com/example/cyclofit/ui/fragment/DistanceCombineChartFragment.kt new file mode 100644 index 0000000..726910d --- /dev/null +++ b/app/src/main/java/com/example/cyclofit/ui/fragment/DistanceCombineChartFragment.kt @@ -0,0 +1,158 @@ +package com.example.cyclofit.ui.fragment + +import android.content.Context +import android.graphics.Color +import android.graphics.Typeface +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.content.res.ResourcesCompat +import androidx.fragment.app.Fragment +import com.example.cyclofit.R +import com.example.cyclofit.databinding.FragmentDistanceCombineChartBinding +import com.example.cyclofit.model.Shared +import com.example.cyclofit.ui.utils.ChartUtils.months +import com.example.cyclofit.ui.utils.Constants +import com.github.mikephil.charting.charts.CombinedChart +import com.github.mikephil.charting.components.AxisBase +import com.github.mikephil.charting.components.Legend +import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.components.YAxis +import com.github.mikephil.charting.data.BarData +import com.github.mikephil.charting.data.BarDataSet +import com.github.mikephil.charting.data.BarEntry +import com.github.mikephil.charting.data.CombinedData +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.data.LineData +import com.github.mikephil.charting.data.LineDataSet +import com.github.mikephil.charting.formatter.ValueFormatter +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import java.lang.reflect.Type + +class DistanceCombineChartFragment : Fragment() { + lateinit var binding: FragmentDistanceCombineChartBinding + var sp = ArrayList() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentDistanceCombineChartBinding.inflate(inflater, container, false) + val sharedPreferences1 = requireContext().getSharedPreferences( + Constants.CYCLOFIT_PREFERENCES, + Context.MODE_PRIVATE + ) + val gson = Gson() + val json = sharedPreferences1.getString("timer", null) + val type: Type = object : TypeToken?>() {}.type + + if (json != null) { + sp = gson.fromJson(json, type) + } + + val distanceArrayList = ArrayList() + for (i in 0 until sp.size) { + distanceArrayList.add("0") + } + val lineDataList = mutableListOf() + val barDataList = mutableListOf() + for ((x, i) in distanceArrayList.withIndex()) { + lineDataList.add(Entry(x.toFloat(), i.toFloat())) + barDataList.add(BarEntry(x.toFloat(), i.toFloat())) + } + val combinedData = CombinedData().apply { + setData(generateLineData(lineDataList)) + setData(generateBarData(barDataList)) + setValueTypeface(Typeface.DEFAULT) + } + setCombinedChart(combinedData) + return binding.root + } + + private fun setCombinedChart(combinedData: CombinedData) { + binding.combinedChart.apply { + data = combinedData + invalidate() + description.isEnabled = false + setBackgroundColor(Color.TRANSPARENT) + setDrawGridBackground(false) + setDrawBarShadow(false) + isHighlightFullBarEnabled = false + // draw bars behind lines + drawOrder = arrayOf( + CombinedChart.DrawOrder.BAR, + CombinedChart.DrawOrder.BUBBLE, + CombinedChart.DrawOrder.CANDLE, + CombinedChart.DrawOrder.LINE, + CombinedChart.DrawOrder.SCATTER + ) + legend.apply { + isWordWrapEnabled = true + verticalAlignment = Legend.LegendVerticalAlignment.BOTTOM + horizontalAlignment = Legend.LegendHorizontalAlignment.CENTER + orientation = Legend.LegendOrientation.HORIZONTAL + setDrawInside(false) + } + axisRight.apply { + setDrawGridLines(false) + setDrawAxisLine(false) + setDrawLabels(false) + axisMinimum = 0f + } + axisLeft.apply { + setDrawGridLines(false) + setDrawAxisLine(false) + setDrawLabels(false) + axisMinimum = 0f + } + val formatter = object : ValueFormatter() { + override fun getAxisLabel(value: Float, axis: AxisBase?): String { + val index = value.toInt() % months.size + return months[index] + } + } + xAxis.apply { + setDrawGridLines(false) + setDrawAxisLine(false) + position = XAxis.XAxisPosition.BOTTOM + axisMinimum = 0f - 0.25f + granularity = 1f + valueFormatter = formatter + axisMaximum = combinedData.xMax + 0.25f + } + } + } + + private fun generateLineData(dataList: MutableList): LineData { + val lineData = LineData() + val lineDataSet = LineDataSet(dataList, "Line DataSet") + lineDataSet.apply { + color = resources.getColor(R.color.purple_200, resources.newTheme()) + valueTextSize = 12f + valueTextColor = Color.BLACK + setDrawValues(true) + mode = LineDataSet.Mode.CUBIC_BEZIER + lineWidth = 2f + axisDependency = YAxis.AxisDependency.LEFT + } + lineData.addDataSet(lineDataSet) + return lineData + } + + private fun generateBarData(dataList: MutableList): BarData { + val dataSet = BarDataSet(dataList, "Bar") + dataSet.apply { + color = ResourcesCompat.getColor(resources, R.color.light_green, resources.newTheme()) + valueTextColor = Color.TRANSPARENT + dataSet.valueTextSize = 10f + axisDependency = YAxis.AxisDependency.LEFT + } + val barWidth = 0.45f // x2 dataset + // (0.45 + 0.02) * 2 + 0.06 = 1.00 -> interval per "group" + val barData = BarData(dataSet) + barData.barWidth = barWidth + return barData + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/cyclofit/ui/fragment/DistancePieChartFragment.kt b/app/src/main/java/com/example/cyclofit/ui/fragment/DistancePieChartFragment.kt new file mode 100644 index 0000000..258419f --- /dev/null +++ b/app/src/main/java/com/example/cyclofit/ui/fragment/DistancePieChartFragment.kt @@ -0,0 +1,124 @@ +package com.example.cyclofit.ui.fragment + +import android.content.Context +import android.graphics.Color +import android.graphics.Typeface +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.example.cyclofit.databinding.FragmentDistancePieChartBinding +import com.example.cyclofit.model.Shared +import com.example.cyclofit.ui.utils.ChartUtils +import com.example.cyclofit.ui.utils.Constants +import com.github.mikephil.charting.animation.Easing +import com.github.mikephil.charting.components.Legend +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.data.PieData +import com.github.mikephil.charting.data.PieDataSet +import com.github.mikephil.charting.data.PieEntry +import com.github.mikephil.charting.formatter.ValueFormatter +import com.github.mikephil.charting.highlight.Highlight +import com.github.mikephil.charting.listener.OnChartValueSelectedListener +import com.github.mikephil.charting.utils.MPPointF +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import java.lang.reflect.Type + +class DistancePieChartFragment : Fragment(), OnChartValueSelectedListener { + lateinit var binding: FragmentDistancePieChartBinding + var sp = ArrayList() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentDistancePieChartBinding.inflate(inflater, container, false) + val sharedPreferences1 = requireContext().getSharedPreferences( + Constants.CYCLOFIT_PREFERENCES, + Context.MODE_PRIVATE + ) + val gson = Gson() + val json = sharedPreferences1.getString("timer", null) + val type: Type = object : TypeToken?>() {}.type + + if (json != null) { + sp = gson.fromJson(json, type) + } + + val distanceArrayList = ArrayList() + for (i in 0 until sp.size) { + distanceArrayList.add("0") + } + val pieDataList = mutableListOf() + for ((x, i) in distanceArrayList.withIndex()) { + pieDataList.add(PieEntry(i.toFloat(), "${i.toFloat()} km")) + } + setPieChart(pieDataList) + return binding.root + } + + private fun setPieChart(dataList: MutableList) { + val dataSet = PieDataSet(dataList, "My Graph") + dataSet.apply { + valueTextSize = 16f + valueTextColor = Color.BLACK + setDrawIcons(false) + sliceSpace = 3f + iconsOffset = MPPointF(0f, 40f) + selectionShift = 5f + colors = ChartUtils.colors + } + + binding.pieChart.apply { + isDrawHoleEnabled = false + description.isEnabled = false + setUsePercentValues(true) + + // entry label text styling + setEntryLabelColor(Color.TRANSPARENT) // hide label text in Pie chart + setEntryLabelTypeface(Typeface.DEFAULT) + setEntryLabelTextSize(16f) + + dragDecelerationFrictionCoef = 0.95f + rotationAngle = 0f + // enable rotation of the chart by touch + isRotationEnabled = true + isHighlightPerTapEnabled = true + + animateY(1400, Easing.EaseInOutQuad) + + legend.apply { + verticalAlignment = Legend.LegendVerticalAlignment.TOP + horizontalAlignment = Legend.LegendHorizontalAlignment.RIGHT + orientation = Legend.LegendOrientation.VERTICAL + setDrawInside(false) + xEntrySpace = 7f + yEntrySpace = 0f + yOffset = 0f + } + val pieData = PieData(dataSet).apply { + setValueFormatter(object : ValueFormatter() { + override fun getPieLabel(value: Float, pieEntry: PieEntry?): String { + return "${String.format("%.1f", value)} %" + } + }) + } + data = PieData(dataSet) + highlightValues(null) // undo all highlights + invalidate() + } + // add a selection listener + binding.pieChart.setOnChartValueSelectedListener(this) + } + + override fun onValueSelected(e: Entry?, h: Highlight?) { + if (e == null) + return + } + + override fun onNothingSelected() { + return + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/cyclofit/ui/fragment/HealthFragment.kt b/app/src/main/java/com/example/cyclofit/ui/fragment/HealthFragment.kt index fba2714..b5b82d7 100644 --- a/app/src/main/java/com/example/cyclofit/ui/fragment/HealthFragment.kt +++ b/app/src/main/java/com/example/cyclofit/ui/fragment/HealthFragment.kt @@ -3,20 +3,23 @@ package com.example.cyclofit.ui.fragment import android.content.Context import android.content.Intent import android.os.Bundle -import androidx.fragment.app.Fragment import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem import android.view.View import android.view.ViewGroup -import androidx.viewpager2.widget.ViewPager2 -import com.example.cyclofit.`interface`.HeartApi +import androidx.core.view.MenuProvider +import com.example.cyclofit.R import com.example.cyclofit.databinding.FragmentHealthBinding +import com.example.cyclofit.`interface`.HeartApi import com.example.cyclofit.model.Shared import com.example.cyclofit.model.User import com.example.cyclofit.ui.activities.GraphActivity -import com.example.cyclofit.ui.adapter.ViewAdapter -import com.example.cyclofit.ui.fragment.HomeFragment.Companion.timer +import com.example.cyclofit.ui.adapter.CombinedChartAdapter +import com.example.cyclofit.ui.adapter.PieChartAdapter +import com.example.cyclofit.ui.adapter.LineChartAdapter import com.example.cyclofit.ui.utils.Constants -import com.github.mikephil.charting.data.Entry import com.google.android.material.tabs.TabLayoutMediator import com.google.firebase.auth.FirebaseAuth import com.google.firebase.firestore.FirebaseFirestore @@ -47,21 +50,22 @@ import java.util.concurrent.TimeUnit savedInstanceState: Bundle? ): View { binding = FragmentHealthBinding.inflate(inflater, container, false) + binding.toolbarHealth.inflateMenu(R.menu.health_top) mFireStore = FirebaseFirestore.getInstance() val pager = binding.viewPager pager.isSaveEnabled = false val tl = binding.tabs - pager.adapter = ViewAdapter(childFragmentManager,lifecycle ) - - TabLayoutMediator(tl,pager){ tab,position-> + pager.adapter = LineChartAdapter(childFragmentManager, lifecycle) + TabLayoutMediator(tl, pager) { tab, position -> tab.text = tabTitle[position] }.attach() binding.cvOfHeartRate.setOnClickListener{ startActivity(Intent(context,GraphActivity::class.java)) } + inflateMenuItem() return binding.root } // private fun fetchCalsBurnt(){ @@ -136,28 +140,59 @@ import java.util.concurrent.TimeUnit val type: Type = object : TypeToken?>() {}.type - if(json!=null) { - sp = gson.fromJson(json, type) - mFireStore.collection(Constants.USERS) - // the document id to get the field of User. - .document(FirebaseAuth.getInstance().currentUser!!.uid) - .get().addOnSuccessListener { - val userWeight = it.toObject(User::class.java)!!.weight - var x: Float = 0F - for (i in sp) { - val y = i.time.toInt() - val MET = 12 // for bicycles - val wt = userWeight.toDouble() // in kg - var totalCalsBurnt = 0.0; - totalCalsBurnt = y * (MET * 3.5 * wt) - totalCalsBurnt /= 200 * 1000; - x += totalCalsBurnt.toFloat() - } - binding.valueOfKcalsBurnt.text = String.format("%.2f", x).toDouble().toString() - } + if(json!=null) { + sp = gson.fromJson(json, type) + mFireStore.collection(Constants.USERS) + // the document id to get the field of User. + .document(FirebaseAuth.getInstance().currentUser!!.uid) + .get().addOnSuccessListener { + val userWeight = it.toObject(User::class.java)!!.weight + var x: Float = 0F + for (i in sp) { + val y = i.time.toInt() + val MET = 12 // for bicycles + val wt = userWeight.toDouble() // in kg + var totalCalsBurnt = 0.0; + totalCalsBurnt = y * (MET * 3.5 * wt) + totalCalsBurnt /= 200 * 1000; + x += totalCalsBurnt.toFloat() + } + binding.valueOfKcalsBurnt.text = String.format("%.2f", x).toDouble().toString() + } } } + private fun inflateMenuItem() { + // use menu provider api to implement menu + binding.toolbarHealth.addMenuProvider(object : MenuProvider { + override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { + // Inflate the menu into the toolbar + } + + override fun onMenuItemSelected(menuItem: MenuItem): Boolean { + return when (menuItem.itemId) { + R.id.line_chart -> { + binding.viewPager.adapter = LineChartAdapter(childFragmentManager, lifecycle) + true + } + + R.id.pie_chart -> { + binding.viewPager.adapter = + PieChartAdapter(childFragmentManager, lifecycle) + true + } + + R.id.combine_chart -> { + binding.viewPager.adapter = + CombinedChartAdapter(childFragmentManager, lifecycle) + true + } + + else -> false + } + } + }) + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/cyclofit/ui/fragment/KcalCombinedChartFragment.kt b/app/src/main/java/com/example/cyclofit/ui/fragment/KcalCombinedChartFragment.kt new file mode 100644 index 0000000..06abea3 --- /dev/null +++ b/app/src/main/java/com/example/cyclofit/ui/fragment/KcalCombinedChartFragment.kt @@ -0,0 +1,183 @@ +package com.example.cyclofit.ui.fragment + +import android.content.Context +import android.graphics.Color +import android.graphics.Typeface +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.content.res.ResourcesCompat +import androidx.fragment.app.Fragment +import com.example.cyclofit.R +import com.example.cyclofit.databinding.FragmentKcalCombinedChartBinding +import com.example.cyclofit.model.Shared +import com.example.cyclofit.model.User +import com.example.cyclofit.ui.utils.ChartUtils.months +import com.example.cyclofit.ui.utils.Constants +import com.github.mikephil.charting.charts.CombinedChart.DrawOrder +import com.github.mikephil.charting.components.AxisBase +import com.github.mikephil.charting.components.Legend +import com.github.mikephil.charting.components.XAxis.XAxisPosition +import com.github.mikephil.charting.components.YAxis +import com.github.mikephil.charting.data.BarData +import com.github.mikephil.charting.data.BarDataSet +import com.github.mikephil.charting.data.BarEntry +import com.github.mikephil.charting.data.CombinedData +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.data.LineData +import com.github.mikephil.charting.data.LineDataSet +import com.github.mikephil.charting.formatter.ValueFormatter +import com.google.firebase.auth.FirebaseAuth +import com.google.firebase.firestore.FirebaseFirestore +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import java.lang.reflect.Type + + +class KcalCombinedChartFragment : Fragment() { + lateinit var binding: FragmentKcalCombinedChartBinding + private var userWeight = "" + private lateinit var mFireStore: FirebaseFirestore + private var sp = ArrayList() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + mFireStore = FirebaseFirestore.getInstance() + binding = FragmentKcalCombinedChartBinding.inflate(inflater, container, false) + val sharedPreferences = requireContext().getSharedPreferences( + Constants.CYCLOFIT_PREFERENCES, + Context.MODE_PRIVATE + ) + val gson = Gson() + val json = sharedPreferences.getString("timer", null) + val type: Type = object : TypeToken?>() {}.type + + //This is for accessing the user data form Firestore collection + mFireStore.collection(Constants.USERS) + // the document id to get the field of User. + .document(FirebaseAuth.getInstance().currentUser!!.uid) + .get().addOnSuccessListener { + userWeight = it.toObject(User::class.java)!!.weight.toDouble().toString() + if (json != null) { + sp = gson.fromJson(json, type) + } + val combinedData = CombinedData().apply { + setData(generateLineData()) + setData(generateBarData()) + setValueTypeface(Typeface.DEFAULT) + } + setCombinedChart(combinedData) + } + return binding.root + } + + private fun getCaloriesForLineChart(userWeight: String): MutableList { + val barArrayList = mutableListOf() + for ((x, i) in sp.withIndex()) { + val y = i.time.toInt() + val met = 12 // for bicycles + val wt = userWeight.toFloat() // in kg + var totalCalsBurnt: Float = y * (met * 3.5f * wt) + totalCalsBurnt /= 200 * 1000 + barArrayList.add(BarEntry(x.toFloat(), totalCalsBurnt)) + } + return barArrayList + } + + private fun getCaloriesForBarChart(userWeight: String): MutableList { + val barArrayList = mutableListOf() + for ((x, i) in sp.withIndex()) { + val y = i.time.toInt() + val met = 12 // for bicycles + val wt = userWeight.toFloat() // in kg + var totalCalsBurnt: Float = y * (met * 3.5f * wt) + totalCalsBurnt /= 200 * 1000 + barArrayList.add(BarEntry(x.toFloat(), totalCalsBurnt)) + } + return barArrayList + } + + private fun setCombinedChart(combinedData: CombinedData) { + binding.combinedChart.apply { + data = combinedData + invalidate() + description.isEnabled = false + setBackgroundColor(Color.TRANSPARENT) + setDrawGridBackground(false) + setDrawBarShadow(false) + isHighlightFullBarEnabled = false + // draw bars behind lines + drawOrder = arrayOf( + DrawOrder.BAR, DrawOrder.BUBBLE, DrawOrder.CANDLE, DrawOrder.LINE, DrawOrder.SCATTER + ) + legend.apply { + isWordWrapEnabled = true + verticalAlignment = Legend.LegendVerticalAlignment.BOTTOM + horizontalAlignment = Legend.LegendHorizontalAlignment.CENTER + orientation = Legend.LegendOrientation.HORIZONTAL + setDrawInside(false) + } + axisRight.apply { + setDrawGridLines(false) + setDrawAxisLine(false) + setDrawLabels(false) + axisMinimum = 0f + } + axisLeft.apply { + setDrawGridLines(false) + setDrawAxisLine(false) + setDrawLabels(false) + axisMinimum = 0f + } + val formatter = object : ValueFormatter() { + override fun getAxisLabel(value: Float, axis: AxisBase?): String { + val index = value.toInt() % months.size + return months[index] + } + } + xAxis.apply { + setDrawGridLines(false) + setDrawAxisLine(false) + position = XAxisPosition.BOTTOM + axisMinimum = 0f - 0.25f + granularity = 1f + valueFormatter = formatter + axisMaximum = combinedData.xMax + 0.25f + } + } + } + + private fun generateLineData(): LineData { + val lineData = LineData() + val lineDataSet = LineDataSet(getCaloriesForLineChart(userWeight), "Line DataSet") + lineDataSet.apply { + color = resources.getColor(R.color.purple_200, resources.newTheme()) + valueTextSize = 12f + valueTextColor = Color.BLACK + setDrawValues(true) + mode = LineDataSet.Mode.CUBIC_BEZIER + lineWidth = 2f + axisDependency = YAxis.AxisDependency.LEFT + } + lineData.addDataSet(lineDataSet) + return lineData + } + + private fun generateBarData(): BarData { + val dataSet = BarDataSet(getCaloriesForBarChart(userWeight), "Bar") + dataSet.apply { + color = ResourcesCompat.getColor(resources, R.color.light_green, resources.newTheme()) + valueTextColor = Color.TRANSPARENT + dataSet.valueTextSize = 10f + axisDependency = YAxis.AxisDependency.LEFT + } + val barWidth = 0.45f // x2 dataset + // (0.45 + 0.02) * 2 + 0.06 = 1.00 -> interval per "group" + val barData = BarData(dataSet) + barData.barWidth = barWidth + return barData + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/cyclofit/ui/fragment/KcalFragment.kt b/app/src/main/java/com/example/cyclofit/ui/fragment/KcalFragment.kt index dc9fdac..7b83860 100644 --- a/app/src/main/java/com/example/cyclofit/ui/fragment/KcalFragment.kt +++ b/app/src/main/java/com/example/cyclofit/ui/fragment/KcalFragment.kt @@ -3,7 +3,6 @@ package com.example.cyclofit.ui.fragment import android.content.Context import android.graphics.Color import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -14,7 +13,6 @@ import com.example.cyclofit.R import com.example.cyclofit.databinding.FragmentKcalBinding import com.example.cyclofit.model.Shared import com.example.cyclofit.model.User -import com.example.cyclofit.ui.firestore.FirestoreClass import com.example.cyclofit.ui.utils.Constants import com.github.mikephil.charting.data.BarEntry import com.github.mikephil.charting.data.Entry @@ -24,14 +22,13 @@ import com.google.firebase.auth.FirebaseAuth import com.google.firebase.firestore.FirebaseFirestore import com.google.gson.Gson import com.google.gson.reflect.TypeToken -import kotlinx.android.synthetic.main.fragment_kcal.* import java.lang.reflect.Type class KcalFragment : Fragment() { lateinit var binding: FragmentKcalBinding private var userWeight = "" - private lateinit var mFireStore : FirebaseFirestore + private lateinit var mFireStore: FirebaseFirestore private var sp = ArrayList() override fun onCreateView( @@ -39,12 +36,13 @@ class KcalFragment : Fragment() { savedInstanceState: Bundle? ): View { mFireStore = FirebaseFirestore.getInstance() - binding= FragmentKcalBinding.inflate(inflater,container,false) + binding = FragmentKcalBinding.inflate(inflater, container, false) val sharedPreferences = requireContext().getSharedPreferences( Constants.CYCLOFIT_PREFERENCES, - Context.MODE_PRIVATE) + Context.MODE_PRIVATE + ) val gson = Gson() - val json = sharedPreferences.getString("timer",null) + val json = sharedPreferences.getString("timer", null) val type: Type = object : TypeToken?>() {}.type //This is for accessing the user data form Firestore collection @@ -52,69 +50,64 @@ class KcalFragment : Fragment() { // the document id to get the field of User. .document(FirebaseAuth.getInstance().currentUser!!.uid) .get().addOnSuccessListener { - userWeight = it.toObject(User::class.java)!!.weight.toDouble().toString() - if(json!=null) { + userWeight = it.toObject(User::class.java)!!.weight.toDouble().toString() + if (json != null) { sp = gson.fromJson(json, type) } - //here the user weight is passed in the getCalories and after calculation the chart is shown - val lineDataSet= LineDataSet(getCalories(userWeight),"My Graph") - val lineData= LineData(lineDataSet) - binding.kcalChart.data=lineData - lineDataSet.color = getColor(resources,R.color.purple_200,resources.newTheme()) - lineDataSet.valueTextColor= Color.WHITE - lineDataSet.valueTextSize=16f - val axisLeft = binding.kcalChart.axisLeft.textColor - binding.kcalChart.axisLeft - lineDataSet.addColor(R.color.white) - lineDataSet.mode=LineDataSet.Mode.CUBIC_BEZIER - binding.kcalChart.description.isEnabled=true - binding.kcalChart.invalidate() - binding.kcalChart.axisRight.apply { - setDrawGridLines(false) - setDrawLabels(false) - setDrawAxisLine(false) - } - binding.kcalChart.axisLeft.apply { - setDrawGridLines(false) - setDrawLabels(false) - setDrawAxisLine(false) - } - binding.kcalChart.xAxis.apply { - setDrawGridLines(false) - setDrawLabels(false) - setDrawAxisLine(false) - } - binding.kcalChart.setDrawGridBackground(false) - binding.kcalChart.setDrawBorders(false) - - kcalChart.data=lineData - lineDataSet.setColor(resources.getColor(R.color.purple_200)) - - lineDataSet.setDrawFilled(true) - lineDataSet.valueTextColor= Color.BLACK - lineDataSet.valueTextSize=16f - kcalChart.description.isEnabled=true - - lineDataSet.setDrawFilled(true) - val drawable = ContextCompat.getDrawable(requireContext(), R.drawable.gradient_health) - lineDataSet.fillDrawable=drawable - + setLineChart() } - return binding.root } private fun getCalories(userWeight: String): MutableList { - val barArrayList=mutableListOf() - for((x, i) in sp.withIndex()){ - val y=i.time.toInt() - val met=12 // for bicycles - val wt=userWeight.toDouble() // in kg - var totalCalsBurnt=0.0; - totalCalsBurnt=y*(met*3.5*wt) - totalCalsBurnt/=200*1000; - barArrayList.add(BarEntry(x*1f,totalCalsBurnt.toFloat()*1f)) + val barArrayList = mutableListOf() + for ((x, i) in sp.withIndex()) { + val y = i.time.toInt() + val met = 12 // for bicycles + val wt = userWeight.toDouble() // in kg + var totalCalsBurnt = 0.0; + totalCalsBurnt = y * (met * 3.5 * wt) + totalCalsBurnt /= 200 * 1000; + barArrayList.add(BarEntry(x * 1f, totalCalsBurnt.toFloat() * 1f)) } return barArrayList } + + private fun setLineChart() { + //here the user weight is passed in the getCalories and after calculation the chart is shown + val lineDataSet = LineDataSet(getCalories(userWeight), "My Graph") + lineDataSet.color = getColor(resources, R.color.purple_200, resources.newTheme()) + lineDataSet.valueTextSize = 16f + lineDataSet.addColor(R.color.white) + lineDataSet.mode = LineDataSet.Mode.CUBIC_BEZIER + lineDataSet.valueTextColor = Color.BLACK + lineDataSet.setDrawFilled(true) + val drawable = + ContextCompat.getDrawable(requireContext(), R.drawable.gradient_health) + lineDataSet.fillDrawable = drawable + val lineData = LineData(lineDataSet) + + binding.kcalChart.apply { + data = lineData + description.isEnabled = false + invalidate() + setDrawGridBackground(false) + setDrawBorders(false) + axisRight.apply { + setDrawGridLines(false) + setDrawLabels(false) + setDrawAxisLine(false) + } + axisLeft.apply { + setDrawGridLines(false) + setDrawLabels(false) + setDrawAxisLine(false) + } + xAxis.apply { + setDrawGridLines(false) + setDrawLabels(false) + setDrawAxisLine(false) + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/cyclofit/ui/fragment/KcalPeiChartFragment.kt b/app/src/main/java/com/example/cyclofit/ui/fragment/KcalPeiChartFragment.kt new file mode 100644 index 0000000..c52f3fa --- /dev/null +++ b/app/src/main/java/com/example/cyclofit/ui/fragment/KcalPeiChartFragment.kt @@ -0,0 +1,148 @@ +package com.example.cyclofit.ui.fragment + +import android.content.Context +import android.graphics.Color +import android.graphics.Typeface +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.example.cyclofit.databinding.FragmentKcalPeiChartBinding +import com.example.cyclofit.model.Shared +import com.example.cyclofit.model.User +import com.example.cyclofit.ui.utils.ChartUtils +import com.example.cyclofit.ui.utils.Constants +import com.github.mikephil.charting.animation.Easing +import com.github.mikephil.charting.components.Legend +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.data.PieData +import com.github.mikephil.charting.data.PieDataSet +import com.github.mikephil.charting.data.PieEntry +import com.github.mikephil.charting.formatter.PercentFormatter +import com.github.mikephil.charting.formatter.ValueFormatter +import com.github.mikephil.charting.highlight.Highlight +import com.github.mikephil.charting.listener.OnChartValueSelectedListener +import com.github.mikephil.charting.utils.MPPointF +import com.google.firebase.auth.FirebaseAuth +import com.google.firebase.firestore.FirebaseFirestore +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import java.lang.reflect.Type + + +class KcalPeiChartFragment : Fragment(), OnChartValueSelectedListener { + lateinit var binding: FragmentKcalPeiChartBinding + private var userWeight = "" + private lateinit var mFireStore: FirebaseFirestore + private var sp = ArrayList() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + mFireStore = FirebaseFirestore.getInstance() + binding = FragmentKcalPeiChartBinding.inflate(inflater, container, false) + val sharedPreferences = requireContext().getSharedPreferences( + Constants.CYCLOFIT_PREFERENCES, + Context.MODE_PRIVATE + ) + val gson = Gson() + val json = sharedPreferences.getString("timer", null) + val type: Type = object : TypeToken?>() {}.type + + //This is for accessing the user data form Firestore collection + mFireStore.collection(Constants.USERS) + // the document id to get the field of User. + .document(FirebaseAuth.getInstance().currentUser!!.uid) + .get().addOnSuccessListener { + userWeight = it.toObject(User::class.java)!!.weight.toDouble().toString() + if (json != null) { + sp = gson.fromJson(json, type) + } + setPieChart(getCalories(userWeight)) + } + return binding.root + } + + private fun getCalories(userWeight: String): MutableList { + val barArrayList = mutableListOf() + for (i in sp) { + val y = i.time.toInt() + val met = 12 // for bicycles + val wt = userWeight.toDouble() // in kg + var totalCalsBurnt: Double = y * (met * 3.5 * wt) + totalCalsBurnt /= 200 * 1000 + barArrayList.add( + PieEntry( + totalCalsBurnt.toFloat(), + "${String.format("%.2f", totalCalsBurnt)} Kcal" + ) + ) + } + return barArrayList + } + + private fun setPieChart(dataList: MutableList) { + val dataSet = PieDataSet(dataList, "My Graph") + dataSet.apply { + valueTextSize = 16f + valueTextColor = Color.BLACK + setDrawIcons(false) + sliceSpace = 3f + iconsOffset = MPPointF(0f, 40f) + selectionShift = 5f + colors = ChartUtils.colors + } + + binding.pieChart.apply { + setUsePercentValues(true) + isDrawHoleEnabled = false + description.isEnabled = false + + // entry label text styling + setEntryLabelColor(Color.TRANSPARENT) + setEntryLabelTypeface(Typeface.DEFAULT) + setEntryLabelTextSize(16f) + + dragDecelerationFrictionCoef = 0.95f + rotationAngle = 0f + // enable rotation of the chart by touch + isRotationEnabled = true + isHighlightPerTapEnabled = true + + animateY(1400, Easing.EaseInOutQuad) + + legend.apply { + verticalAlignment = Legend.LegendVerticalAlignment.TOP + horizontalAlignment = Legend.LegendHorizontalAlignment.RIGHT + orientation = Legend.LegendOrientation.VERTICAL + setDrawInside(false) + xEntrySpace = 7f + yEntrySpace = 0f + yOffset = 0f + } + val pieData = PieData(dataSet).apply { + setValueFormatter(object : ValueFormatter() { + override fun getPieLabel(value: Float, pieEntry: PieEntry?): String { + return "${String.format("%.1f", value)} %" + } + }) + } + data = pieData + highlightValues(null) // undo all highlights + invalidate() + } + // add a selection listener + binding.pieChart.setOnChartValueSelectedListener(this) + } + + override fun onValueSelected(e: Entry?, h: Highlight?) { + if (e == null) + return + } + + override fun onNothingSelected() { + return + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/cyclofit/ui/fragment/TimeCombinedChartFragment.kt b/app/src/main/java/com/example/cyclofit/ui/fragment/TimeCombinedChartFragment.kt new file mode 100644 index 0000000..d12342c --- /dev/null +++ b/app/src/main/java/com/example/cyclofit/ui/fragment/TimeCombinedChartFragment.kt @@ -0,0 +1,156 @@ +package com.example.cyclofit.ui.fragment + +import android.content.Context +import android.graphics.Color +import android.graphics.Typeface +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.content.res.ResourcesCompat +import androidx.fragment.app.Fragment +import com.example.cyclofit.R +import com.example.cyclofit.databinding.FragmentTimeCombinedChartBinding +import com.example.cyclofit.model.Shared +import com.example.cyclofit.ui.utils.ChartUtils.months +import com.example.cyclofit.ui.utils.Constants +import com.github.mikephil.charting.charts.CombinedChart +import com.github.mikephil.charting.components.AxisBase +import com.github.mikephil.charting.components.Legend +import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.components.YAxis +import com.github.mikephil.charting.data.BarData +import com.github.mikephil.charting.data.BarDataSet +import com.github.mikephil.charting.data.BarEntry +import com.github.mikephil.charting.data.CombinedData +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.data.LineData +import com.github.mikephil.charting.data.LineDataSet +import com.github.mikephil.charting.formatter.ValueFormatter +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import java.lang.reflect.Type + +class TimeCombinedChartFragment : Fragment() { + lateinit var binding: FragmentTimeCombinedChartBinding + var sp = ArrayList() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentTimeCombinedChartBinding.inflate(inflater, container, false) + + val sharedPreferences = requireContext().getSharedPreferences( + Constants.CYCLOFIT_PREFERENCES, + Context.MODE_PRIVATE + ) + val gson = Gson() + val json = sharedPreferences.getString("timer", null) + + val type: Type = object : TypeToken?>() {}.type + + if (json != null) { + sp = gson.fromJson(json, type) + } + + val lineDataList = mutableListOf() + val barDataList = mutableListOf() + for ((x, i) in sp.withIndex()) { + lineDataList.add(Entry(x.toFloat(), i.time.toFloat())) + barDataList.add(BarEntry(x.toFloat(), i.time.toFloat())) + } + val combinedData = CombinedData().apply { + setData(generateLineData(lineDataList)) + setData(generateBarData(barDataList)) + setValueTypeface(Typeface.DEFAULT) + } + setCombinedChart(combinedData) + return binding.root + } + + private fun setCombinedChart(combinedData: CombinedData) { + binding.combinedChart.apply { + data = combinedData + invalidate() + description.isEnabled = false + setBackgroundColor(Color.TRANSPARENT) + setDrawGridBackground(false) + setDrawBarShadow(false) + isHighlightFullBarEnabled = false + // draw bars behind lines + drawOrder = arrayOf( + CombinedChart.DrawOrder.BAR, + CombinedChart.DrawOrder.BUBBLE, + CombinedChart.DrawOrder.CANDLE, + CombinedChart.DrawOrder.LINE, + CombinedChart.DrawOrder.SCATTER + ) + legend.apply { + isWordWrapEnabled = true + verticalAlignment = Legend.LegendVerticalAlignment.BOTTOM + horizontalAlignment = Legend.LegendHorizontalAlignment.CENTER + orientation = Legend.LegendOrientation.HORIZONTAL + setDrawInside(false) + } + axisRight.apply { + setDrawGridLines(false) + setDrawAxisLine(false) + setDrawLabels(false) + axisMinimum = 0f + } + axisLeft.apply { + setDrawGridLines(false) + setDrawAxisLine(false) + setDrawLabels(false) + axisMinimum = 0f + } + val formatter = object : ValueFormatter() { + override fun getAxisLabel(value: Float, axis: AxisBase?): String { + val index = value.toInt() % months.size + return months[index] + } + } + xAxis.apply { + setDrawGridLines(false) + setDrawAxisLine(false) + position = XAxis.XAxisPosition.BOTTOM + axisMinimum = 0f - 0.25f + granularity = 1f + valueFormatter = formatter + axisMaximum = combinedData.xMax + 0.25f + } + } + } + + private fun generateLineData(dataList: MutableList): LineData { + val lineData = LineData() + val lineDataSet = LineDataSet(dataList, "Line DataSet") + lineDataSet.apply { + color = resources.getColor(R.color.purple_200, resources.newTheme()) + valueTextSize = 12f + valueTextColor = Color.BLACK + setDrawValues(true) + mode = LineDataSet.Mode.CUBIC_BEZIER + lineWidth = 2f + axisDependency = YAxis.AxisDependency.LEFT + } + lineData.addDataSet(lineDataSet) + return lineData + } + + private fun generateBarData(dataList: MutableList): BarData { + val dataSet = BarDataSet(dataList, "Bar") + dataSet.apply { + color = ResourcesCompat.getColor(resources, R.color.light_green, resources.newTheme()) + valueTextColor = Color.TRANSPARENT + dataSet.valueTextSize = 10f + axisDependency = YAxis.AxisDependency.LEFT + } + val barWidth = 0.45f // x2 dataset + // (0.45 + 0.02) * 2 + 0.06 = 1.00 -> interval per "group" + val barData = BarData(dataSet) + barData.barWidth = barWidth + return barData + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/cyclofit/ui/fragment/TimePieCharFragment.kt b/app/src/main/java/com/example/cyclofit/ui/fragment/TimePieCharFragment.kt new file mode 100644 index 0000000..f4ab6b9 --- /dev/null +++ b/app/src/main/java/com/example/cyclofit/ui/fragment/TimePieCharFragment.kt @@ -0,0 +1,123 @@ +package com.example.cyclofit.ui.fragment + +import android.content.Context +import android.graphics.Color +import android.graphics.Typeface +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.example.cyclofit.databinding.FragmentTimePieCharBinding +import com.example.cyclofit.model.Shared +import com.example.cyclofit.ui.utils.ChartUtils +import com.example.cyclofit.ui.utils.Constants +import com.github.mikephil.charting.animation.Easing +import com.github.mikephil.charting.components.Legend +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.data.PieData +import com.github.mikephil.charting.data.PieDataSet +import com.github.mikephil.charting.data.PieEntry +import com.github.mikephil.charting.formatter.ValueFormatter +import com.github.mikephil.charting.highlight.Highlight +import com.github.mikephil.charting.listener.OnChartValueSelectedListener +import com.github.mikephil.charting.utils.MPPointF +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import java.lang.reflect.Type + + +class TimePieCharFragment : Fragment(), OnChartValueSelectedListener { + lateinit var binding: FragmentTimePieCharBinding + var sp = ArrayList() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentTimePieCharBinding.inflate(inflater, container, false) + + val sharedPreferences = requireContext().getSharedPreferences( + Constants.CYCLOFIT_PREFERENCES, + Context.MODE_PRIVATE + ) + val gson = Gson() + val json = sharedPreferences.getString("timer", null) + + val type: Type = object : TypeToken?>() {}.type + + if (json != null) { + sp = gson.fromJson(json, type) + } + + val pieDataList = mutableListOf() + for ((x, i) in sp.withIndex()) { + pieDataList.add(PieEntry(i.time.toFloat(), "${i.time} sec")) + } + setPieChart(pieDataList) + return binding.root + } + + private fun setPieChart(dataList: MutableList) { + val dataSet = PieDataSet(dataList, "My Graph") + dataSet.apply { + valueTextSize = 16f + valueTextColor = Color.BLACK + setDrawIcons(false) + sliceSpace = 3f + iconsOffset = MPPointF(0f, 40f) + selectionShift = 5f + colors = ChartUtils.colors + } + + binding.pieChart.apply { + isDrawHoleEnabled = false + description.isEnabled = false + setUsePercentValues(true) + + // entry label text styling + setEntryLabelColor(Color.TRANSPARENT) // hide label text in Pie chart + setEntryLabelTypeface(Typeface.DEFAULT) + setEntryLabelTextSize(16f) + + dragDecelerationFrictionCoef = 0.95f + rotationAngle = 0f + // enable rotation of the chart by touch + isRotationEnabled = true + isHighlightPerTapEnabled = true + + animateY(1400, Easing.EaseInOutQuad) + + legend.apply { + verticalAlignment = Legend.LegendVerticalAlignment.TOP + horizontalAlignment = Legend.LegendHorizontalAlignment.RIGHT + orientation = Legend.LegendOrientation.VERTICAL + setDrawInside(false) + xEntrySpace = 7f + yEntrySpace = 0f + yOffset = 0f + } + val pieData = PieData(dataSet).apply { + setValueFormatter(object : ValueFormatter() { + override fun getPieLabel(value: Float, pieEntry: PieEntry?): String { + return "${String.format("%.1f", value)} %" + } + }) + } + data = pieData + highlightValues(null) // undo all highlights + invalidate() + } + // add a selection listener + binding.pieChart.setOnChartValueSelectedListener(this) + } + + override fun onValueSelected(e: Entry?, h: Highlight?) { + if (e == null) + return + } + + override fun onNothingSelected() { + return + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/cyclofit/ui/utils/ChartUtils.kt b/app/src/main/java/com/example/cyclofit/ui/utils/ChartUtils.kt new file mode 100644 index 0000000..901e58b --- /dev/null +++ b/app/src/main/java/com/example/cyclofit/ui/utils/ChartUtils.kt @@ -0,0 +1,20 @@ +package com.example.cyclofit.ui.utils + +import com.github.mikephil.charting.utils.ColorTemplate + +object ChartUtils { + + // add a lot of colors + val colors = ArrayList().apply { + for (c in ColorTemplate.VORDIPLOM_COLORS) add(c) + for (c in ColorTemplate.JOYFUL_COLORS) add(c) + for (c in ColorTemplate.COLORFUL_COLORS) add(c) + for (c in ColorTemplate.LIBERTY_COLORS) add(c) + for (c in ColorTemplate.PASTEL_COLORS) add(c) + add(ColorTemplate.getHoloBlue()) + } + + val months = arrayOf( + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec" + ) +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_distance_combine_chart.xml b/app/src/main/res/layout/fragment_distance_combine_chart.xml new file mode 100644 index 0000000..5c35fbc --- /dev/null +++ b/app/src/main/res/layout/fragment_distance_combine_chart.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_distance_pie_chart.xml b/app/src/main/res/layout/fragment_distance_pie_chart.xml new file mode 100644 index 0000000..03d92d1 --- /dev/null +++ b/app/src/main/res/layout/fragment_distance_pie_chart.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_health.xml b/app/src/main/res/layout/fragment_health.xml index e8b3333..ddff6f1 100644 --- a/app/src/main/res/layout/fragment_health.xml +++ b/app/src/main/res/layout/fragment_health.xml @@ -1,45 +1,63 @@ - - android:scrollbars="none" - tools:context=".ui.fragment.HealthFragment"> - - - + android:layout_height="200dp" + android:background="@drawable/background_img" + android:gravity="center" + android:orientation="vertical"> + + + + + + + + + + - - - - - - + android:layout_alignParentBottom="true" + android:background="@color/backgroundColor" + android:gravity="center"> @@ -140,41 +158,43 @@ android:id="@+id/cvOfHeartRate" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical" android:gravity="center" + android:orientation="vertical" > + + android:text="0 bpm" + android:textSize="35sp" /> + + android:textSize="18sp" /> @@ -205,17 +225,17 @@ + android:backgroundTint="@color/dark_green" + app:cardCornerRadius="10dp" + app:cardElevation="15dp"> + android:layout_toRightOf="@id/kcalsBurnt" + android:backgroundTint="@color/dark_green" + app:cardCornerRadius="10dp" + app:cardElevation="15dp"> - + + - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_kcal_combined_chart.xml b/app/src/main/res/layout/fragment_kcal_combined_chart.xml new file mode 100644 index 0000000..8845b12 --- /dev/null +++ b/app/src/main/res/layout/fragment_kcal_combined_chart.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_kcal_pei_chart.xml b/app/src/main/res/layout/fragment_kcal_pei_chart.xml new file mode 100644 index 0000000..bf3edd9 --- /dev/null +++ b/app/src/main/res/layout/fragment_kcal_pei_chart.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_time_combined_chart.xml b/app/src/main/res/layout/fragment_time_combined_chart.xml new file mode 100644 index 0000000..e11ecbc --- /dev/null +++ b/app/src/main/res/layout/fragment_time_combined_chart.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_time_pie_char.xml b/app/src/main/res/layout/fragment_time_pie_char.xml new file mode 100644 index 0000000..6eedc4e --- /dev/null +++ b/app/src/main/res/layout/fragment_time_pie_char.xml @@ -0,0 +1,12 @@ + + + + /> + \ No newline at end of file diff --git a/app/src/main/res/menu/health_top.xml b/app/src/main/res/menu/health_top.xml new file mode 100644 index 0000000..1d10346 --- /dev/null +++ b/app/src/main/res/menu/health_top.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file