mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
Workout details: combined charts activity
This commit is contained in:
@@ -198,6 +198,10 @@
|
||||
android:name=".activities.maps.MapsTrackActivity"
|
||||
android:label="@string/maps"
|
||||
android:parentActivityName=".activities.SettingsActivity" />
|
||||
<activity
|
||||
android:name=".activities.workouts.charts.WorkoutChartsActivity"
|
||||
android:label="@string/workout_charts"
|
||||
android:parentActivityName=".activities.SettingsActivity" />
|
||||
<activity
|
||||
android:name=".activities.SleepAsAndroidPreferencesActivity"
|
||||
android:label="@string/sleepasandroid_settings"
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.activities.charts;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_SECONDS_PER_KM;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_MINUTES_PER_KM;
|
||||
|
||||
import com.github.mikephil.charting.formatter.ValueFormatter;
|
||||
|
||||
@@ -17,7 +17,7 @@ public class SpeedYLabelFormatter extends ValueFormatter {
|
||||
|
||||
@Override
|
||||
public String getFormattedValue(float value) {
|
||||
if (unit.equals(UNIT_SECONDS_PER_KM)) {
|
||||
if (unit.equals(UNIT_MINUTES_PER_KM)) {
|
||||
value = value > 0 ? Math.round((60 / (value * 3.6)) * 60) : 0;
|
||||
}
|
||||
return workoutValueFormatter.formatValue(value, unit, false);
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.activities.charts.marker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.github.mikephil.charting.components.MarkerView;
|
||||
import com.github.mikephil.charting.data.Entry;
|
||||
import com.github.mikephil.charting.data.LineData;
|
||||
import com.github.mikephil.charting.formatter.ValueFormatter;
|
||||
import com.github.mikephil.charting.highlight.Highlight;
|
||||
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
|
||||
import com.github.mikephil.charting.utils.MPPointF;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
|
||||
public class ValueMarker extends MarkerView {
|
||||
private TextView markerContent;
|
||||
private List<ValueFormatter> valueFormatters;
|
||||
private List<String> valueUnits;
|
||||
private LineData lineData;
|
||||
|
||||
public ValueMarker(Context context) {
|
||||
super(context, R.layout.value_marker);
|
||||
this.markerContent = findViewById(R.id.marker_content);
|
||||
}
|
||||
|
||||
public ValueMarker(Context context, LineData lineData, List<ValueFormatter> valueFormatters, List<String> valueUnits) {
|
||||
super(context, R.layout.value_marker);
|
||||
this.markerContent = findViewById(R.id.marker_content);
|
||||
this.valueFormatters = valueFormatters;
|
||||
this.lineData = lineData;
|
||||
this.valueUnits = valueUnits;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshContent(Entry e, Highlight highlight) {
|
||||
float xVal = e.getX();
|
||||
StringBuilder content = new StringBuilder();
|
||||
for (int i = 0; i < lineData.getDataSetCount(); i++) {
|
||||
ILineDataSet dataSet = lineData.getDataSetByIndex(i);
|
||||
if (dataSet == null || !dataSet.isVisible()) {
|
||||
continue;
|
||||
}
|
||||
Entry entryForX = dataSet.getEntryForXValue(xVal, Float.NaN);
|
||||
if (entryForX != null) {
|
||||
if (valueFormatters.get(i) != null) {
|
||||
content.append(valueFormatters.get(i).getFormattedValue(entryForX.getY()));
|
||||
} else {
|
||||
content.append(entryForX.getY());
|
||||
}
|
||||
if (valueUnits.get(i) != null) {
|
||||
content.append(" ");
|
||||
content.append(valueUnits.get(i));
|
||||
}
|
||||
content.append("\n");
|
||||
}
|
||||
}
|
||||
markerContent.setText(content.toString().trim());
|
||||
super.refreshContent(e, highlight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas, float posX, float posY) {
|
||||
// Stick the value marker box to the top.
|
||||
MPPointF offset = getOffset();
|
||||
float markerWidth = getWidth();
|
||||
float offsetX = offset.x;
|
||||
|
||||
if ((posX + offsetX) < 0) {
|
||||
posX = -offsetX;
|
||||
} else if ((posX + offsetX + markerWidth) > canvas.getWidth()) {
|
||||
posX = canvas.getWidth() - markerWidth - offsetX;
|
||||
}
|
||||
|
||||
float fixedTopY = 10f;
|
||||
canvas.translate(posX + offsetX, fixedTopY);
|
||||
draw(canvas);
|
||||
canvas.translate(-posX - offsetX, -fixedTopY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MPPointF getOffset() {
|
||||
// Center horizontally and place above the marker point
|
||||
return new MPPointF(-(getWidth() / 2f), -getHeight());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+30
-3
@@ -27,6 +27,7 @@ import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
@@ -46,7 +47,12 @@ import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.github.mikephil.charting.charts.LineChart
|
||||
import com.github.mikephil.charting.components.XAxis
|
||||
import com.github.mikephil.charting.data.Entry
|
||||
import com.github.mikephil.charting.data.LineData
|
||||
import com.github.mikephil.charting.highlight.Highlight
|
||||
import com.github.mikephil.charting.listener.ChartTouchListener
|
||||
import com.github.mikephil.charting.listener.OnChartGestureListener
|
||||
import com.github.mikephil.charting.listener.OnChartValueSelectedListener
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -54,9 +60,13 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication
|
||||
import nodomain.freeyourgadget.gadgetbridge.R
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.ActivitySummariesChartFragment
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.charts.DurationXLabelFormatter
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.charts.marker.ValueMarker
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.fit.FitViewerActivity
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.maps.MapsTrackActivity
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.maps.MapsTrackViewModel.Companion.getActivityPoints
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.workouts.charts.ChartDataRepository
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.workouts.charts.DefaultWorkoutCharts
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.workouts.charts.WorkoutChartsActivity
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.workouts.entries.ActivitySummaryEntry
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.workouts.entries.ActivitySummaryGroup
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.workouts.entries.ActivitySummarySimpleEntry
|
||||
@@ -282,7 +292,7 @@ class WorkoutDetailsFragment : Fragment(), MenuProvider {
|
||||
}
|
||||
|
||||
groupCharts.forEach { chart ->
|
||||
addChart(binding.summaryDetails, false, chart)
|
||||
addChart(binding.summaryDetails, false, chart, workout.charts)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -381,7 +391,7 @@ class WorkoutDetailsFragment : Fragment(), MenuProvider {
|
||||
binding.dynamicCharts.removeAllViews()
|
||||
for (chart in workout.charts) {
|
||||
if (chart.group == null) {
|
||||
addChart(binding.dynamicCharts, true, chart)
|
||||
addChart(binding.dynamicCharts, true, chart, workout.charts)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,7 +406,8 @@ class WorkoutDetailsFragment : Fragment(), MenuProvider {
|
||||
private fun addChart(
|
||||
chartsLayout: LinearLayout,
|
||||
includeHeader: Boolean,
|
||||
chart: WorkoutChart
|
||||
chart: WorkoutChart,
|
||||
allChartsData: List<WorkoutChart>
|
||||
) {
|
||||
if (includeHeader) {
|
||||
val chartTitle = TextView(context).apply {
|
||||
@@ -464,6 +475,22 @@ class WorkoutDetailsFragment : Fragment(), MenuProvider {
|
||||
}
|
||||
lineChart.data = chart.chartData as LineData?
|
||||
lineChart.description.isEnabled = false;
|
||||
lineChart.onChartGestureListener = object : OnChartGestureListener {
|
||||
override fun onChartLongPressed(me: MotionEvent?) {}
|
||||
override fun onChartGestureStart(me: MotionEvent?, lastPerformedGesture: ChartTouchListener.ChartGesture?) {}
|
||||
override fun onChartGestureEnd(me: MotionEvent?, lastPerformedGesture: ChartTouchListener.ChartGesture?) {}
|
||||
override fun onChartSingleTapped(me: MotionEvent?) {
|
||||
ChartDataRepository.chartData = allChartsData
|
||||
val intent = Intent(requireContext(), WorkoutChartsActivity::class.java).apply {
|
||||
putExtra(WorkoutChartsActivity.INIT_CHART_ID, chart.id)
|
||||
}
|
||||
startActivity(intent)
|
||||
}
|
||||
override fun onChartDoubleTapped(me: MotionEvent?) {}
|
||||
override fun onChartFling(me1: MotionEvent?, me2: MotionEvent?, velocityX: Float, velocityY: Float) {}
|
||||
override fun onChartScale(me: MotionEvent?, scaleX: Float, scaleY: Float) {}
|
||||
override fun onChartTranslate(me: MotionEvent?, dX: Float, dY: Float) {}
|
||||
}
|
||||
lineChart.invalidate()
|
||||
chartsFragmentHolder.addView(lineChart)
|
||||
|
||||
|
||||
+2
@@ -6,6 +6,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_LB;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_METERS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_METERS_PER_SECOND;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_MINUTES_PER_KM;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_MM;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_SECONDS_PER_100_METERS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_SECONDS_PER_100_YARDS;
|
||||
@@ -122,6 +123,7 @@ public class WorkoutValueFormatter {
|
||||
}
|
||||
break;
|
||||
case UNIT_SECONDS_PER_KM:
|
||||
case UNIT_MINUTES_PER_KM:
|
||||
if (units.equals(UNIT_IMPERIAL)) {
|
||||
value = value / 60D * 1.609344;
|
||||
unit = "minutes_mi";
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.activities.workouts.charts
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.workout.WorkoutChart
|
||||
|
||||
object ChartDataRepository {
|
||||
var chartData: List<WorkoutChart>? = null
|
||||
|
||||
fun clear() {
|
||||
chartData = null
|
||||
}
|
||||
}
|
||||
+13
-5
@@ -3,6 +3,7 @@ package nodomain.freeyourgadget.gadgetbridge.activities.workouts.charts;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_BPM;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_METERS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_METERS_PER_SECOND;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_MINUTES_PER_KM;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_SECONDS_PER_KM;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -12,6 +13,7 @@ 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.github.mikephil.charting.formatter.ValueFormatter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
@@ -56,25 +58,31 @@ public class DefaultWorkoutCharts {
|
||||
if (!heartRateDataPoints.isEmpty()) {
|
||||
final String label = String.format("%s(%s)", context.getString(R.string.heart_rate), getUnitString(context, UNIT_BPM));
|
||||
final LineDataSet dataset = createDataSet(context, heartRateDataPoints, label, context.getResources().getColor(R.color.chart_line_heart_rate));
|
||||
charts.add(new WorkoutChart(context.getString(R.string.heart_rate), ActivitySummaryEntries.GROUP_HEART_RATE, new LineData(dataset)));
|
||||
ValueFormatter integerFormatter = new ValueFormatter() {
|
||||
@Override
|
||||
public String getFormattedValue(float value) {
|
||||
return String.valueOf((int) value);
|
||||
}
|
||||
};
|
||||
charts.add(new WorkoutChart("heart_rate", context.getString(R.string.heart_rate), ActivitySummaryEntries.GROUP_HEART_RATE, new LineData(dataset), integerFormatter, getUnitString(context, UNIT_BPM)));
|
||||
}
|
||||
|
||||
if (hasSpeedValues && !speedDataPoints.isEmpty()) {
|
||||
if (ActivityKind.isPaceActivity(activityKind)) {
|
||||
final String label = String.format("%s (%s)", context.getString(R.string.Pace), getUnitString(context, UNIT_SECONDS_PER_KM));
|
||||
final String label = String.format("%s (%s)", context.getString(R.string.Pace), getUnitString(context, UNIT_MINUTES_PER_KM));
|
||||
final LineDataSet dataset = createDataSet(context, speedDataPoints, label, context.getResources().getColor(R.color.chart_line_speed));
|
||||
charts.add(new WorkoutChart(context.getString(R.string.Pace), ActivitySummaryEntries.GROUP_SPEED, new LineData(dataset), new SpeedYLabelFormatter(UNIT_SECONDS_PER_KM)));
|
||||
charts.add(new WorkoutChart("pace", context.getString(R.string.Pace), ActivitySummaryEntries.GROUP_SPEED, new LineData(dataset), new SpeedYLabelFormatter(UNIT_MINUTES_PER_KM), getUnitString(context, UNIT_MINUTES_PER_KM)));
|
||||
} else {
|
||||
final String label = String.format("%s (%s)", context.getString(R.string.Speed), getUnitString(context, UNIT_METERS_PER_SECOND));
|
||||
final LineDataSet dataset = createDataSet(context, speedDataPoints, label, context.getResources().getColor(R.color.chart_line_speed));
|
||||
charts.add(new WorkoutChart(context.getString(R.string.Speed), ActivitySummaryEntries.GROUP_SPEED, new LineData(dataset), new SpeedYLabelFormatter(UNIT_METERS_PER_SECOND)));
|
||||
charts.add(new WorkoutChart("speed", context.getString(R.string.Speed), ActivitySummaryEntries.GROUP_SPEED, new LineData(dataset), new SpeedYLabelFormatter(UNIT_METERS_PER_SECOND), getUnitString(context, UNIT_METERS_PER_SECOND)));
|
||||
}
|
||||
}
|
||||
|
||||
if (!elevationDataPoints.isEmpty()) {
|
||||
final String label = String.format("%s (%s)", context.getString(R.string.Elevation), getUnitString(context, UNIT_METERS));
|
||||
LineDataSet dataset = createDataSet(context, elevationDataPoints, label, context.getResources().getColor(R.color.chart_line_elevation));
|
||||
charts.add(new WorkoutChart(context.getString(R.string.Elevation), ActivitySummaryEntries.GROUP_ELEVATION, new LineData(dataset)));
|
||||
charts.add(new WorkoutChart("elevation", context.getString(R.string.Elevation), ActivitySummaryEntries.GROUP_ELEVATION, new LineData(dataset), null, getUnitString(context, UNIT_METERS)));
|
||||
}
|
||||
|
||||
return charts;
|
||||
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.activities.workouts.charts
|
||||
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import com.github.mikephil.charting.components.XAxis
|
||||
import com.github.mikephil.charting.components.YAxis
|
||||
import com.github.mikephil.charting.data.LineData
|
||||
import com.github.mikephil.charting.data.LineDataSet
|
||||
import com.github.mikephil.charting.formatter.DefaultAxisValueFormatter
|
||||
import com.github.mikephil.charting.formatter.ValueFormatter
|
||||
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet
|
||||
import com.google.android.material.chip.Chip
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication
|
||||
import nodomain.freeyourgadget.gadgetbridge.R
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.charts.DurationXLabelFormatter
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.charts.marker.ValueMarker
|
||||
import nodomain.freeyourgadget.gadgetbridge.databinding.WorkoutChartsBinding
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.workout.WorkoutChart
|
||||
import kotlin.math.max
|
||||
|
||||
class WorkoutChartsActivity : AbstractGBActivity() {
|
||||
private lateinit var binding: WorkoutChartsBinding
|
||||
private var chartData: List<WorkoutChart>? = null
|
||||
val selectedCharts = mutableListOf<Any>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = WorkoutChartsBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
chartData = ChartDataRepository.chartData
|
||||
|
||||
if (chartData == null) {
|
||||
Toast.makeText(this, "No charts data found", Toast.LENGTH_SHORT).show()
|
||||
finish()
|
||||
return
|
||||
}
|
||||
ChartDataRepository.clear()
|
||||
|
||||
val chartTextColor = GBApplication.getSecondaryTextColor(baseContext);
|
||||
binding.workoutDataChart.xAxis.apply {
|
||||
setDrawLabels(true)
|
||||
setDrawGridLines(false)
|
||||
setDrawLimitLinesBehindData(true)
|
||||
isEnabled = true
|
||||
textColor = chartTextColor
|
||||
position = XAxis.XAxisPosition.BOTTOM
|
||||
valueFormatter = DurationXLabelFormatter("mm:ss")
|
||||
}
|
||||
binding.workoutDataChart.axisLeft.apply {
|
||||
setDrawGridLines(false)
|
||||
setDrawTopYLabelEntry(true)
|
||||
textColor = chartTextColor
|
||||
isEnabled = true
|
||||
}
|
||||
binding.workoutDataChart.axisRight.apply {
|
||||
setDrawGridLines(false)
|
||||
setDrawTopYLabelEntry(true)
|
||||
textColor = chartTextColor
|
||||
isEnabled = true
|
||||
}
|
||||
binding.workoutDataChart.description.isEnabled = false;
|
||||
binding.workoutDataChart.legend.textColor = GBApplication.getTextColor(baseContext);
|
||||
|
||||
val initChartId = intent.getStringExtra(INIT_CHART_ID) ?: "none"
|
||||
selectedCharts.add(0, initChartId);
|
||||
setupChipGroup(initChartId);
|
||||
refreshChart()
|
||||
}
|
||||
|
||||
fun setupChipGroup(initChartId: String) {
|
||||
for (chart in chartData!!) {
|
||||
val chip = Chip(this).apply {
|
||||
text = chart.title
|
||||
isCheckable = true
|
||||
isClickable = true
|
||||
tag = chart.id
|
||||
isChecked = chart.id.equals(initChartId)
|
||||
}
|
||||
chip.setOnCheckedChangeListener { _, isChecked ->
|
||||
val tag = chip.tag
|
||||
if (isChecked) {
|
||||
if (selectedCharts.size >= 2) {
|
||||
chip.isChecked = false
|
||||
Toast.makeText(this, "You can only compare two items at a time.", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
selectedCharts.add(tag)
|
||||
refreshChart()
|
||||
}
|
||||
} else {
|
||||
if (selectedCharts.size == 1 && selectedCharts.contains(tag)) {
|
||||
chip.isChecked = true
|
||||
Toast.makeText(this, "There should be at least one item selected.", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
selectedCharts.remove(tag)
|
||||
refreshChart()
|
||||
}
|
||||
}
|
||||
}
|
||||
binding.workoutDataChartChipGroup.addView(chip)
|
||||
}
|
||||
}
|
||||
|
||||
fun refreshChart() {
|
||||
val lineDataSets = mutableListOf<ILineDataSet>()
|
||||
val lineDataSetsMarkerFormatters = mutableListOf<ValueFormatter?>()
|
||||
val lineDataSetsMarkerUnits = mutableListOf<String?>()
|
||||
var leftY = true
|
||||
selectedCharts.forEach { selectedChart ->
|
||||
val workoutChart = chartData?.find { it.id == selectedChart } ?: return@forEach
|
||||
val dataSet = workoutChart.chartData.getDataSetByIndex(0) as? LineDataSet ?: return@forEach
|
||||
dataSet.highLightColor = this.getColor(R.color.chart_highline_dolor)
|
||||
dataSet.highlightLineWidth = 1f;
|
||||
dataSet.axisDependency = if(leftY) YAxis.AxisDependency.LEFT else YAxis.AxisDependency.RIGHT;
|
||||
lineDataSets.add(dataSet)
|
||||
lineDataSetsMarkerFormatters.add(workoutChart.chartYLabelFormatter);
|
||||
lineDataSetsMarkerUnits.add(workoutChart.unitString);
|
||||
val axis = if (leftY) binding.workoutDataChart.axisLeft else binding.workoutDataChart.axisRight
|
||||
axis.valueFormatter = workoutChart.chartYLabelFormatter ?: DefaultAxisValueFormatter(0)
|
||||
leftY = false
|
||||
}
|
||||
if (selectedCharts.size == 1) {
|
||||
val selectedChartId = selectedCharts.first()
|
||||
val workoutChart = chartData?.find { it.id == selectedChartId } ?: return
|
||||
binding.workoutDataChart.axisRight.valueFormatter = workoutChart?.chartYLabelFormatter ?: DefaultAxisValueFormatter(0)
|
||||
}
|
||||
val lineData = LineData(lineDataSets)
|
||||
binding.workoutDataChart.data = lineData;
|
||||
binding.workoutDataChart.marker = ValueMarker(this, lineData, lineDataSetsMarkerFormatters, lineDataSetsMarkerUnits);
|
||||
binding.workoutDataChart.highlightValues(null)
|
||||
binding.workoutDataChart.invalidate()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val INIT_CHART_ID = "INIT_CHART_ID"
|
||||
}
|
||||
}
|
||||
+1
@@ -195,6 +195,7 @@ public class ActivitySummaryEntries {
|
||||
public static final String UNIT_SECONDS = "seconds";
|
||||
public static final String UNIT_MILLISECONDS = "milliseconds_ms";
|
||||
public static final String UNIT_SECONDS_PER_KM = "seconds_km";
|
||||
public static final String UNIT_MINUTES_PER_KM = "minutes_km";
|
||||
public static final String UNIT_SECONDS_PER_M = "seconds_m";
|
||||
public static final String UNIT_METERS_PER_SECOND = "meters_second";
|
||||
public static final String UNIT_KMPH = "km_h";
|
||||
|
||||
@@ -4,8 +4,10 @@ import com.github.mikephil.charting.data.ChartData
|
||||
import com.github.mikephil.charting.formatter.ValueFormatter
|
||||
|
||||
data class WorkoutChart @JvmOverloads constructor(
|
||||
val id: String,
|
||||
val title: String,
|
||||
val group: String,
|
||||
val chartData: ChartData<*>,
|
||||
var chartYLabelFormatter: ValueFormatter? = null,
|
||||
var unitString: String? = null,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="?android:windowBackground" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@android:color/holo_orange_dark" />
|
||||
</shape>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<TextView
|
||||
android:id="@+id/marker_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textSize="14sp"
|
||||
android:textColor="?attr/textColorPrimary"
|
||||
android:background="@drawable/marker_value_background"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="5dp"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,27 @@
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:background="?android:attr/windowBackground"
|
||||
android:orientation="vertical"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="nodomain.freeyourgadget.gadgetbridge.activities.workouts.charts.WorkoutChartsActivity">
|
||||
|
||||
|
||||
<com.github.mikephil.charting.charts.LineChart
|
||||
android:id="@+id/workout_data_chart"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="300dp"
|
||||
android:layout_alignParentTop="true"
|
||||
/>
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/workout_data_chart_chip_group"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/workout_data_chart"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginTop="8dp"
|
||||
/>
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -92,6 +92,7 @@
|
||||
<color name="chart_line_speed" type="color">#80D8FF</color>
|
||||
<color name="chart_line_elevation" type="color">#66BB6A</color>
|
||||
<color name="chart_line_heart_rate" type="color">#F44336</color>
|
||||
<color name="chart_highline_dolor" type="color">#ffff8800</color>
|
||||
|
||||
<attr name="row_separator" format="color" />
|
||||
<color name="alternate_row_background_light">#FFEDEDED</color>
|
||||
|
||||
@@ -4151,6 +4151,7 @@
|
||||
<string name="huawei_better_audio_quality_title">Prioritise better audio quality.</string>
|
||||
<string name="huawei_better_audio_quality_summary">Enable better audio codecs (LDAC). This should improve sound quality, but it increases power consumption and can cause delays.</string>
|
||||
<string name="maps">Maps</string>
|
||||
<string name="workout_charts">Workout Charts</string>
|
||||
<string name="maps_settings">Maps settings</string>
|
||||
<string name="maps_download_title">Download Maps</string>
|
||||
<string name="maps_download_summary">Click here to download map files. They should be saved to the folder selected in the preference below.</string>
|
||||
|
||||
Reference in New Issue
Block a user