mirror of
https://github.com/russok/FitoTrack.git
synced 2025-10-29 00:32:11 -07:00
Migrate the workout saving process to the extra class WorkoutSaver
This commit is contained in:
parent
7617809897
commit
83c0168108
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -19,118 +19,10 @@
|
|||||||
|
|
||||||
package de.tadris.fitness.data;
|
package de.tadris.fitness.data;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.hardware.SensorManager;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import de.tadris.fitness.Instance;
|
|
||||||
import de.tadris.fitness.util.CalorieCalculator;
|
|
||||||
|
|
||||||
public class WorkoutManager {
|
public class WorkoutManager {
|
||||||
|
|
||||||
public static void insertWorkout(Context context, Workout workout, List<WorkoutSample> samples){
|
|
||||||
AppDatabase db= Instance.getInstance(context).db;
|
|
||||||
|
|
||||||
|
|
||||||
workout.id= System.currentTimeMillis();
|
|
||||||
|
|
||||||
// Delete Samples with same time
|
|
||||||
for(int i= samples.size()-2; i >= 0; i--){
|
|
||||||
WorkoutSample sample= samples.get(i);
|
|
||||||
WorkoutSample lastSample= samples.get(i+1);
|
|
||||||
if(sample.absoluteTime == lastSample.absoluteTime){
|
|
||||||
samples.remove(lastSample);
|
|
||||||
Log.i("WorkoutManager", "Removed samples at " + sample.absoluteTime + " rel: " + sample.relativeTime + "; " + lastSample.relativeTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculating values
|
|
||||||
double length= 0;
|
|
||||||
for(int i= 1; i < samples.size(); i++){
|
|
||||||
double sampleLength= samples.get(i - 1).toLatLong().sphericalDistance(samples.get(i).toLatLong());
|
|
||||||
long timeDiff= (samples.get(i).relativeTime - samples.get(i - 1).relativeTime) / 1000;
|
|
||||||
length+= sampleLength;
|
|
||||||
samples.get(i).speed= Math.abs(sampleLength / timeDiff);
|
|
||||||
}
|
|
||||||
workout.length= (int)length;
|
|
||||||
workout.avgSpeed= ((double) workout.length) / ((double) workout.duration / 1000);
|
|
||||||
workout.avgPace= ((double)workout.duration / 1000 / 60) / ((double) workout.length / 1000);
|
|
||||||
workout.calorie= CalorieCalculator.calculateCalories(workout, Instance.getInstance(context).userPreferences.getUserWeight());
|
|
||||||
|
|
||||||
// Setting workoutId in the samples
|
|
||||||
int i= 0;
|
|
||||||
double topSpeed= 0;
|
|
||||||
double elevationSum= 0; // Sum of elevation
|
|
||||||
double pressureSum= 0; // Sum of elevation
|
|
||||||
for(WorkoutSample sample : samples){
|
|
||||||
i++;
|
|
||||||
sample.id= workout.id + i;
|
|
||||||
sample.workoutId= workout.id;
|
|
||||||
elevationSum+= sample.elevation;
|
|
||||||
pressureSum+= sample.tmpPressure;
|
|
||||||
if(sample.speed > topSpeed){
|
|
||||||
topSpeed= sample.speed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
workout.topSpeed= topSpeed;
|
|
||||||
|
|
||||||
// Calculating height data
|
|
||||||
boolean pressureDataAvailable= samples.get(0).tmpPressure != -1;
|
|
||||||
double avgElevation= elevationSum / samples.size();
|
|
||||||
double avgPressure= pressureSum / samples.size();
|
|
||||||
|
|
||||||
workout.ascent = 0;
|
|
||||||
workout.descent = 0;
|
|
||||||
|
|
||||||
for(i= 0; i < samples.size(); i++){
|
|
||||||
WorkoutSample sample= samples.get(i);
|
|
||||||
|
|
||||||
if(pressureDataAvailable){
|
|
||||||
// Altitude Difference to Average Elevation in meters
|
|
||||||
float altitude_difference =
|
|
||||||
SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, sample.tmpPressure) -
|
|
||||||
SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, (float) avgPressure);
|
|
||||||
sample.elevation= avgElevation + altitude_difference;
|
|
||||||
} // Else: use already set GPS elevation in WorkoutSample.elevation
|
|
||||||
}
|
|
||||||
|
|
||||||
int range= 3;
|
|
||||||
for(i= 0; i < samples.size(); i++){
|
|
||||||
int min= Math.max(i-range, 0);
|
|
||||||
int max= Math.min(i+range, samples.size()-1);
|
|
||||||
samples.get(i).tmpElevation= getAverageElevation(samples.subList(min, max));
|
|
||||||
}
|
|
||||||
|
|
||||||
for(i= 0; i < samples.size(); i++) {
|
|
||||||
WorkoutSample sample = samples.get(i);
|
|
||||||
sample.elevation= sample.tmpElevation;
|
|
||||||
if(i >= 1){
|
|
||||||
WorkoutSample lastSample= samples.get(i-1);
|
|
||||||
double diff= sample.elevation - lastSample.elevation;
|
|
||||||
if(diff > 0){
|
|
||||||
workout.ascent += diff;
|
|
||||||
}else{
|
|
||||||
workout.descent += Math.abs(diff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Saving workout and samples
|
|
||||||
db.workoutDao().insertWorkoutAndSamples(workout, samples.toArray(new WorkoutSample[0]));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double getAverageElevation(List<WorkoutSample> samples){
|
|
||||||
double sum= 0;
|
|
||||||
for(WorkoutSample sample : samples){
|
|
||||||
sum+= sample.elevation;
|
|
||||||
}
|
|
||||||
return sum / samples.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void roundSpeedValues(List<WorkoutSample> samples){
|
public static void roundSpeedValues(List<WorkoutSample> samples){
|
||||||
for(int i= 0; i < samples.size(); i++){
|
for(int i= 0; i < samples.size(); i++){
|
||||||
WorkoutSample sample= samples.get(i);
|
WorkoutSample sample= samples.get(i);
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -31,7 +31,6 @@ import java.util.List;
|
|||||||
|
|
||||||
import de.tadris.fitness.Instance;
|
import de.tadris.fitness.Instance;
|
||||||
import de.tadris.fitness.data.Workout;
|
import de.tadris.fitness.data.Workout;
|
||||||
import de.tadris.fitness.data.WorkoutManager;
|
|
||||||
import de.tadris.fitness.data.WorkoutSample;
|
import de.tadris.fitness.data.WorkoutSample;
|
||||||
import de.tadris.fitness.util.CalorieCalculator;
|
import de.tadris.fitness.util.CalorieCalculator;
|
||||||
|
|
||||||
@ -53,7 +52,7 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
|
|||||||
/**
|
/**
|
||||||
* Time after which the workout is stopped and saved automatically because there is no activity anymore
|
* Time after which the workout is stopped and saved automatically because there is no activity anymore
|
||||||
*/
|
*/
|
||||||
private static final int AUTO_STOP_TIMEOUT= 1000*60*60*20;
|
private static final int AUTO_STOP_TIMEOUT= 1000*60*60*20; // 20 minutes
|
||||||
|
|
||||||
private Context context;
|
private Context context;
|
||||||
private Workout workout;
|
private Workout workout;
|
||||||
@ -65,7 +64,7 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
|
|||||||
private long lastPause= 0;
|
private long lastPause= 0;
|
||||||
private long lastSampleTime= 0;
|
private long lastSampleTime= 0;
|
||||||
private double distance= 0;
|
private double distance= 0;
|
||||||
private boolean hasBegan = false;
|
private boolean hasBegun = false;
|
||||||
|
|
||||||
private static final double SIGNAL_BAD_THRESHOLD= 20; // In meters
|
private static final double SIGNAL_BAD_THRESHOLD= 20; // In meters
|
||||||
private static final int SIGNAL_LOST_THRESHOLD= 10000; // In milliseconds
|
private static final int SIGNAL_LOST_THRESHOLD= 10000; // In milliseconds
|
||||||
@ -135,7 +134,7 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
|
|||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}).start();
|
}, "WorkoutWatchdog").start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkSignalState(){
|
private void checkSignalState(){
|
||||||
@ -193,7 +192,7 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
|
|||||||
}
|
}
|
||||||
Log.i("Recorder", "Save");
|
Log.i("Recorder", "Save");
|
||||||
synchronized (samples){
|
synchronized (samples){
|
||||||
WorkoutManager.insertWorkout(context, workout, samples);
|
new WorkoutSaver(context, workout, samples).saveWorkout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,6 +208,8 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
|
|||||||
if(isActive()){
|
if(isActive()){
|
||||||
double distance= 0;
|
double distance= 0;
|
||||||
if(getSampleCount() > 0){
|
if(getSampleCount() > 0){
|
||||||
|
// Checks whether the minimum distance to last sample was reached
|
||||||
|
// and if the time difference to the last sample is too small
|
||||||
synchronized (samples){
|
synchronized (samples){
|
||||||
WorkoutSample lastSample= samples.get(samples.size() - 1);
|
WorkoutSample lastSample= samples.get(samples.size() - 1);
|
||||||
distance= LocationListener.locationToLatLong(location).sphericalDistance(new LatLong(lastSample.lat, lastSample.lon));
|
distance= LocationListener.locationToLatLong(location).sphericalDistance(new LatLong(lastSample.lat, lastSample.lon));
|
||||||
@ -220,18 +221,17 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
|
|||||||
}
|
}
|
||||||
lastSampleTime= System.currentTimeMillis();
|
lastSampleTime= System.currentTimeMillis();
|
||||||
if(state == RecordingState.RUNNING && location.getTime() > workout.start){
|
if(state == RecordingState.RUNNING && location.getTime() > workout.start){
|
||||||
if(samples.size() == 2 && !hasBegan){
|
if(samples.size() == 2 && !hasBegun){
|
||||||
lastResume= System.currentTimeMillis();
|
initialClearValues();
|
||||||
workout.start= System.currentTimeMillis();
|
hasBegun = true; // Do not clear a second time
|
||||||
lastPause= 0;
|
|
||||||
time= 0;
|
|
||||||
pauseTime= 0;
|
|
||||||
this.distance= 0;
|
|
||||||
samples.clear();
|
|
||||||
|
|
||||||
hasBegan = true; // Do not clear a second time
|
|
||||||
}
|
}
|
||||||
this.distance+= distance;
|
this.distance+= distance;
|
||||||
|
addToSamples(location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addToSamples(Location location){
|
||||||
WorkoutSample sample= new WorkoutSample();
|
WorkoutSample sample= new WorkoutSample();
|
||||||
sample.lat= location.getLatitude();
|
sample.lat= location.getLatitude();
|
||||||
sample.lon= location.getLongitude();
|
sample.lon= location.getLongitude();
|
||||||
@ -247,9 +247,16 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
|
|||||||
synchronized (samples){
|
synchronized (samples){
|
||||||
samples.add(sample);
|
samples.add(sample);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
private void initialClearValues(){
|
||||||
}
|
lastResume= System.currentTimeMillis();
|
||||||
|
workout.start= System.currentTimeMillis();
|
||||||
|
lastPause= 0;
|
||||||
|
time= 0;
|
||||||
|
pauseTime= 0;
|
||||||
|
this.distance= 0;
|
||||||
|
samples.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
183
app/src/main/java/de/tadris/fitness/recording/WorkoutSaver.java
Normal file
183
app/src/main/java/de/tadris/fitness/recording/WorkoutSaver.java
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||||
|
*
|
||||||
|
* This file is part of FitoTrack
|
||||||
|
*
|
||||||
|
* FitoTrack is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* FitoTrack is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.tadris.fitness.recording;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.hardware.SensorManager;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.tadris.fitness.Instance;
|
||||||
|
import de.tadris.fitness.data.AppDatabase;
|
||||||
|
import de.tadris.fitness.data.Workout;
|
||||||
|
import de.tadris.fitness.data.WorkoutSample;
|
||||||
|
import de.tadris.fitness.util.CalorieCalculator;
|
||||||
|
|
||||||
|
public class WorkoutSaver {
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
private Workout workout;
|
||||||
|
private List<WorkoutSample> samples;
|
||||||
|
private AppDatabase db;
|
||||||
|
|
||||||
|
public WorkoutSaver(Context context, Workout workout, List<WorkoutSample> samples) {
|
||||||
|
this.context = context;
|
||||||
|
this.workout = workout;
|
||||||
|
this.samples = samples;
|
||||||
|
db= Instance.getInstance(context).db;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveWorkout(){
|
||||||
|
setIds();
|
||||||
|
clearSamplesWithSameTime();
|
||||||
|
setSimpleValues();
|
||||||
|
setTopSpeed();
|
||||||
|
|
||||||
|
setRealElevation();
|
||||||
|
setAscentAndDescent();
|
||||||
|
|
||||||
|
storeInDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setIds(){
|
||||||
|
workout.id= System.currentTimeMillis();
|
||||||
|
int i= 0;
|
||||||
|
for(WorkoutSample sample : samples) {
|
||||||
|
i++;
|
||||||
|
sample.id = workout.id + i;
|
||||||
|
sample.workoutId = workout.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearSamplesWithSameTime(){
|
||||||
|
for(int i= samples.size()-2; i >= 0; i--){
|
||||||
|
WorkoutSample sample= samples.get(i);
|
||||||
|
WorkoutSample lastSample= samples.get(i+1);
|
||||||
|
if(sample.absoluteTime == lastSample.absoluteTime){
|
||||||
|
samples.remove(lastSample);
|
||||||
|
Log.i("WorkoutManager", "Removed samples at " + sample.absoluteTime + " rel: " + sample.relativeTime + "; " + lastSample.relativeTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setSimpleValues(){
|
||||||
|
double length= 0;
|
||||||
|
for(int i= 1; i < samples.size(); i++){
|
||||||
|
double sampleLength= samples.get(i - 1).toLatLong().sphericalDistance(samples.get(i).toLatLong());
|
||||||
|
long timeDiff= (samples.get(i).relativeTime - samples.get(i - 1).relativeTime) / 1000;
|
||||||
|
length+= sampleLength;
|
||||||
|
samples.get(i).speed= Math.abs(sampleLength / timeDiff);
|
||||||
|
}
|
||||||
|
workout.length= (int)length;
|
||||||
|
workout.avgSpeed= ((double) workout.length) / ((double) workout.duration / 1000);
|
||||||
|
workout.avgPace= ((double)workout.duration / 1000 / 60) / ((double) workout.length / 1000);
|
||||||
|
workout.calorie= CalorieCalculator.calculateCalories(workout, Instance.getInstance(context).userPreferences.getUserWeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setTopSpeed(){
|
||||||
|
double topSpeed= 0;
|
||||||
|
for(WorkoutSample sample : samples){
|
||||||
|
if(sample.speed > topSpeed){
|
||||||
|
topSpeed= sample.speed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
workout.topSpeed= topSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setRealElevation(){
|
||||||
|
boolean pressureDataAvailable= samples.get(0).tmpPressure != -1;
|
||||||
|
|
||||||
|
if(!pressureDataAvailable){
|
||||||
|
// Because pressure data isn't available we just use the use GPS elevation
|
||||||
|
// in WorkoutSample.elevation which was already set
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double avgElevation= getAverageElevation();
|
||||||
|
double avgPressure= getAveragePressure();
|
||||||
|
|
||||||
|
for(int i= 0; i < samples.size(); i++){
|
||||||
|
WorkoutSample sample= samples.get(i);
|
||||||
|
|
||||||
|
// Altitude Difference to Average Elevation in meters
|
||||||
|
float altitude_difference =
|
||||||
|
SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, sample.tmpPressure) -
|
||||||
|
SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, (float) avgPressure);
|
||||||
|
sample.elevation= avgElevation + altitude_difference;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private double getAverageElevation(){
|
||||||
|
return getAverageElevation(samples);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double getAverageElevation(List<WorkoutSample> samples){
|
||||||
|
double elevationSum= 0; // Sum of elevation
|
||||||
|
for(WorkoutSample sample : samples){
|
||||||
|
elevationSum+= sample.elevation;
|
||||||
|
}
|
||||||
|
|
||||||
|
return elevationSum / samples.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private double getAveragePressure(){
|
||||||
|
double pressureSum= 0;
|
||||||
|
for(WorkoutSample sample : samples){
|
||||||
|
pressureSum+= sample.tmpPressure;
|
||||||
|
}
|
||||||
|
return pressureSum / samples.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setAscentAndDescent(){
|
||||||
|
workout.ascent = 0;
|
||||||
|
workout.descent = 0;
|
||||||
|
|
||||||
|
// First calculate a floating average to eliminate pressure noise to influence our ascent/descent
|
||||||
|
int range= 3;
|
||||||
|
for(int i= 0; i < samples.size(); i++){
|
||||||
|
int min= Math.max(i-range, 0);
|
||||||
|
int max= Math.min(i+range, samples.size()-1);
|
||||||
|
samples.get(i).tmpElevation= getAverageElevation(samples.subList(min, max));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now sum up the ascent/descent
|
||||||
|
for(int i= 0; i < samples.size(); i++) {
|
||||||
|
WorkoutSample sample = samples.get(i);
|
||||||
|
sample.elevation= sample.tmpElevation;
|
||||||
|
if(i >= 1){
|
||||||
|
WorkoutSample lastSample= samples.get(i-1);
|
||||||
|
double diff= sample.elevation - lastSample.elevation;
|
||||||
|
if(diff > 0){
|
||||||
|
// If this sample is higher than the last one, add difference to ascent
|
||||||
|
workout.ascent += diff;
|
||||||
|
}else{
|
||||||
|
// If this sample is lower than the last one, add difference to descent
|
||||||
|
workout.descent += Math.abs(diff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void storeInDatabase(){
|
||||||
|
db.workoutDao().insertWorkoutAndSamples(workout, samples.toArray(new WorkoutSample[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user