Migrate the workout saving process to the extra class WorkoutSaver

This commit is contained in:
jannis 2020-01-03 14:23:08 +01:00
parent 7617809897
commit 83c0168108
3 changed files with 223 additions and 141 deletions

View File

@ -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
*
@ -19,118 +19,10 @@
package de.tadris.fitness.data;
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.util.CalorieCalculator;
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){
for(int i= 0; i < samples.size(); i++){
WorkoutSample sample= samples.get(i);

View File

@ -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
*
@ -31,7 +31,6 @@ import java.util.List;
import de.tadris.fitness.Instance;
import de.tadris.fitness.data.Workout;
import de.tadris.fitness.data.WorkoutManager;
import de.tadris.fitness.data.WorkoutSample;
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
*/
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 Workout workout;
@ -65,7 +64,7 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
private long lastPause= 0;
private long lastSampleTime= 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 int SIGNAL_LOST_THRESHOLD= 10000; // In milliseconds
@ -135,7 +134,7 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}, "WorkoutWatchdog").start();
}
private void checkSignalState(){
@ -193,7 +192,7 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
}
Log.i("Recorder", "Save");
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()){
double distance= 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){
WorkoutSample lastSample= samples.get(samples.size() - 1);
distance= LocationListener.locationToLatLong(location).sphericalDistance(new LatLong(lastSample.lat, lastSample.lon));
@ -220,38 +221,44 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
}
lastSampleTime= System.currentTimeMillis();
if(state == RecordingState.RUNNING && location.getTime() > workout.start){
if(samples.size() == 2 && !hasBegan){
lastResume= System.currentTimeMillis();
workout.start= System.currentTimeMillis();
lastPause= 0;
time= 0;
pauseTime= 0;
this.distance= 0;
samples.clear();
hasBegan = true; // Do not clear a second time
if(samples.size() == 2 && !hasBegun){
initialClearValues();
hasBegun = true; // Do not clear a second time
}
this.distance+= distance;
WorkoutSample sample= new WorkoutSample();
sample.lat= location.getLatitude();
sample.lon= location.getLongitude();
sample.elevation= location.getAltitude();
sample.speed= location.getSpeed();
sample.relativeTime= location.getTime() - workout.start - pauseTime;
sample.absoluteTime= location.getTime();
if(Instance.getInstance(context).pressureAvailable){
sample.tmpPressure= Instance.getInstance(context).lastPressure;
}else{
sample.tmpPressure= -1;
}
synchronized (samples){
samples.add(sample);
}
addToSamples(location);
}
}
}
private void addToSamples(Location location){
WorkoutSample sample= new WorkoutSample();
sample.lat= location.getLatitude();
sample.lon= location.getLongitude();
sample.elevation= location.getAltitude();
sample.speed= location.getSpeed();
sample.relativeTime= location.getTime() - workout.start - pauseTime;
sample.absoluteTime= location.getTime();
if(Instance.getInstance(context).pressureAvailable){
sample.tmpPressure= Instance.getInstance(context).lastPressure;
}else{
sample.tmpPressure= -1;
}
synchronized (samples){
samples.add(sample);
}
}
private void initialClearValues(){
lastResume= System.currentTimeMillis();
workout.start= System.currentTimeMillis();
lastPause= 0;
time= 0;
pauseTime= 0;
this.distance= 0;
samples.clear();
}
/**
* Returns the distance in meters
*/

View 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]));
}
}