mirror of
https://github.com/russok/FitoTrack.git
synced 2025-10-29 00:32:11 -07:00
Merge branch '1.1'
This commit is contained in:
commit
9c17d19308
@ -35,8 +35,8 @@ android {
|
||||
applicationId "de.tadris.fitness"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 28
|
||||
versionCode 103
|
||||
versionName "1.0.3"
|
||||
versionCode 110
|
||||
versionName "1.1.0"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
buildTypes {
|
||||
|
||||
@ -1,4 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019 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/>.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="de.tadris.fitness">
|
||||
@ -46,7 +65,8 @@
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/filepaths" />
|
||||
</provider>
|
||||
<service android:name=".location.LocationListener"></service>
|
||||
<service android:name=".recording.LocationListener" />
|
||||
<service android:name=".recording.PressureService" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@ -21,14 +21,17 @@ package de.tadris.fitness;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.Room;
|
||||
import androidx.room.migration.Migration;
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.tadris.fitness.data.AppDatabase;
|
||||
import de.tadris.fitness.data.UserPreferences;
|
||||
import de.tadris.fitness.location.LocationListener;
|
||||
import de.tadris.fitness.recording.LocationListener;
|
||||
import de.tadris.fitness.util.unit.UnitUtils;
|
||||
|
||||
public class Instance {
|
||||
@ -48,9 +51,45 @@ public class Instance {
|
||||
public List<LocationListener.LocationChangeListener> locationChangeListeners= new ArrayList<>();
|
||||
public UserPreferences userPreferences;
|
||||
|
||||
public boolean pressureAvailable= false;
|
||||
public float lastPressure= 0;
|
||||
|
||||
private Instance(Context context) {
|
||||
userPreferences= new UserPreferences(context);
|
||||
db = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, DATABASE_NAME)
|
||||
.addMigrations(new Migration(1, 2) {
|
||||
@Override
|
||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||
try{
|
||||
database.beginTransaction();
|
||||
|
||||
database.execSQL("ALTER table workout add descent REAL NOT NULL DEFAULT 0;");
|
||||
database.execSQL("ALTER table workout add ascent REAL NOT NULL DEFAULT 0");
|
||||
|
||||
database.execSQL("ALTER TABLE workout_sample RENAME TO workout_sample2;");
|
||||
|
||||
database.execSQL("CREATE TABLE workout_sample (" +
|
||||
"id INTEGER NOT NULL DEFAULT NULL PRIMARY KEY," +
|
||||
"relativeTime INTEGER NOT NULL DEFAULT NULL," +
|
||||
"elevation REAL NOT NULL DEFAULT NULL," +
|
||||
"absoluteTime INTEGER NOT NULL DEFAULT NULL," +
|
||||
"lat REAL NOT NULL DEFAULT NULL," +
|
||||
"lon REAL NOT NULL DEFAULT NULL," +
|
||||
"speed REAL NOT NULL DEFAULT NULL," +
|
||||
"workout_id INTEGER NOT NULL DEFAULT NULL," +
|
||||
"FOREIGN KEY (workout_id) REFERENCES workout(id) ON DELETE CASCADE);");
|
||||
|
||||
database.execSQL("INSERT INTO workout_sample (id, relativeTime, elevation, absoluteTime, lat, lon, speed, workout_id) " +
|
||||
"SELECT id, relativeTime, elevation, absoluteTime, lat, lon, speed, workout_id FROM workout_sample2");
|
||||
|
||||
database.execSQL("DROP TABLE workout_sample2");
|
||||
|
||||
database.setTransactionSuccessful();
|
||||
}finally {
|
||||
database.endTransaction();
|
||||
}
|
||||
}
|
||||
})
|
||||
.allowMainThreadQueries()
|
||||
.build();
|
||||
UnitUtils.setUnit(context);
|
||||
|
||||
@ -21,9 +21,11 @@ package de.tadris.fitness.activity;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
@ -50,10 +52,11 @@ import java.util.List;
|
||||
import de.tadris.fitness.Instance;
|
||||
import de.tadris.fitness.R;
|
||||
import de.tadris.fitness.data.Workout;
|
||||
import de.tadris.fitness.location.LocationListener;
|
||||
import de.tadris.fitness.location.WorkoutRecorder;
|
||||
import de.tadris.fitness.map.MapManager;
|
||||
import de.tadris.fitness.map.tilesource.TileSources;
|
||||
import de.tadris.fitness.recording.LocationListener;
|
||||
import de.tadris.fitness.recording.PressureService;
|
||||
import de.tadris.fitness.recording.WorkoutRecorder;
|
||||
import de.tadris.fitness.util.ThemeManager;
|
||||
import de.tadris.fitness.util.unit.UnitUtils;
|
||||
|
||||
@ -72,6 +75,7 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
||||
private Handler mHandler= new Handler();
|
||||
PowerManager.WakeLock wakeLock;
|
||||
Intent locationListener;
|
||||
Intent pressureService;
|
||||
private boolean saved= false;
|
||||
|
||||
@Override
|
||||
@ -154,7 +158,10 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
||||
}).start();
|
||||
}
|
||||
|
||||
int i= 0;
|
||||
|
||||
private void updateDescription(){
|
||||
i++;
|
||||
timeView.setText(UnitUtils.getHourMinuteSecondTime(recorder.getDuration()));
|
||||
infoViews[0].setText(getString(R.string.workoutDistance), UnitUtils.getDistance(recorder.getDistance()));
|
||||
infoViews[1].setText(getString(R.string.workoutBurnedEnergy), recorder.getCalories() + " kcal");
|
||||
@ -228,19 +235,42 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
||||
|
||||
public void stopListener(){
|
||||
stopService(locationListener);
|
||||
stopService(pressureService);
|
||||
}
|
||||
|
||||
public void startListener(){
|
||||
if(locationListener == null){
|
||||
locationListener= new Intent(this, LocationListener.class);
|
||||
pressureService= new Intent(this, PressureService.class);
|
||||
}else{
|
||||
stopListener();
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
startForegroundService(locationListener);
|
||||
startService(pressureService);
|
||||
}else{
|
||||
startService(locationListener);
|
||||
startService(pressureService);
|
||||
}
|
||||
checkGpsStatus();
|
||||
}
|
||||
|
||||
private void checkGpsStatus(){
|
||||
final LocationManager manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
|
||||
|
||||
if (!manager.isProviderEnabled(LocationManager.GPS_PROVIDER)){
|
||||
openDialogNoGps();
|
||||
}
|
||||
}
|
||||
|
||||
private void openDialogNoGps(){
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.noGpsTitle)
|
||||
.setMessage(R.string.noGpsMessage)
|
||||
.setNegativeButton(R.string.cancel, (dialog, which) -> finish())
|
||||
.setPositiveButton(R.string.enable, (dialog, which) -> startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS)))
|
||||
.setCancelable(false)
|
||||
.create().show();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -325,9 +325,7 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
public boolean onMenuItemSelected(int featureId, MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
if (id == android.R.id.home) {
|
||||
if (!super.onMenuItemSelected(featureId, item)) {
|
||||
NavUtils.navigateUpFromSameTask(this);
|
||||
}
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
return super.onMenuItemSelected(featureId, item);
|
||||
|
||||
@ -131,6 +131,13 @@ public class ShowWorkoutActivity extends FitoTrackActivity {
|
||||
addKeyValue(getString(R.string.workoutTotalEnergy), workout.calorie + " kcal",
|
||||
getString(R.string.workoutEnergyConsumption), UnitUtils.getRelativeEnergyConsumption((double)workout.calorie / ((double)workout.length / 1000)));
|
||||
|
||||
addTitle(getString(R.string.height));
|
||||
|
||||
addKeyValue(getString(R.string.workoutAscent), UnitUtils.getDistance((int)workout.ascent),
|
||||
getString(R.string.workoutDescent), UnitUtils.getDistance((int)workout.descent));
|
||||
|
||||
addHeightDiagram();
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -199,20 +206,20 @@ public class ShowWorkoutActivity extends FitoTrackActivity {
|
||||
root.addView(v);
|
||||
}
|
||||
|
||||
void addSpeedDiagram(){
|
||||
void addDiagram(SampleConverter converter){
|
||||
LineChart chart= new LineChart(this);
|
||||
|
||||
WorkoutManager.roundSpeedValues(samples);
|
||||
converter.onCreate();
|
||||
|
||||
List<Entry> entries = new ArrayList<>();
|
||||
for (WorkoutSample sample : samples) {
|
||||
// turn your data into Entry objects
|
||||
Entry e= new Entry((float)(sample.relativeTime) / 1000f / 60f, (float)sample.tmpRoundedSpeed*3.6f);
|
||||
Entry e= new Entry((float)(sample.relativeTime) / 1000f / 60f, converter.getValue(sample));
|
||||
entries.add(e);
|
||||
sample.tmpEntry= e;
|
||||
converter.sampleGetsEntry(sample, e);
|
||||
}
|
||||
|
||||
LineDataSet dataSet = new LineDataSet(entries, "Speed"); // add entries to dataset // TODO: localisatoin
|
||||
LineDataSet dataSet = new LineDataSet(entries, converter.getName()); // add entries to dataset
|
||||
dataSet.setColor(getThemePrimaryColor());
|
||||
dataSet.setValueTextColor(getThemePrimaryColor());
|
||||
dataSet.setDrawCircles(false);
|
||||
@ -220,11 +227,12 @@ public class ShowWorkoutActivity extends FitoTrackActivity {
|
||||
dataSet.setMode(LineDataSet.Mode.CUBIC_BEZIER);
|
||||
|
||||
Description description= new Description();
|
||||
description.setText("min - km/h");
|
||||
description.setText(converter.getDescription());
|
||||
|
||||
LineData lineData = new LineData(dataSet);
|
||||
chart.setData(lineData);
|
||||
chart.setScaleEnabled(false);
|
||||
chart.setScaleXEnabled(true);
|
||||
chart.setScaleYEnabled(false);
|
||||
chart.setDescription(description);
|
||||
chart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() {
|
||||
@Override
|
||||
@ -232,7 +240,7 @@ public class ShowWorkoutActivity extends FitoTrackActivity {
|
||||
onNothingSelected();
|
||||
Paint p= AndroidGraphicFactory.INSTANCE.createPaint();
|
||||
p.setColor(Color.BLUE);
|
||||
highlightingCircle= new FixedPixelCircle(getSamplebyTime(e).toLatLong(), 10, p, null);
|
||||
highlightingCircle= new FixedPixelCircle(findSample(converter, e).toLatLong(), 10, p, null);
|
||||
map.addLayer(highlightingCircle);
|
||||
}
|
||||
|
||||
@ -248,9 +256,84 @@ public class ShowWorkoutActivity extends FitoTrackActivity {
|
||||
root.addView(chart, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getWindowManager().getDefaultDisplay().getWidth()*3/4));
|
||||
}
|
||||
|
||||
WorkoutSample getSamplebyTime(Entry entry){
|
||||
interface SampleConverter{
|
||||
void onCreate();
|
||||
float getValue(WorkoutSample sample);
|
||||
void sampleGetsEntry(WorkoutSample sample, Entry entry);
|
||||
String getName();
|
||||
String getDescription();
|
||||
boolean compare(WorkoutSample sample, Entry entry);
|
||||
}
|
||||
|
||||
void addHeightDiagram(){
|
||||
addDiagram(new SampleConverter() {
|
||||
@Override
|
||||
public void onCreate() { }
|
||||
|
||||
@Override
|
||||
public float getValue(WorkoutSample sample) {
|
||||
return (float)UnitUtils.CHOSEN_SYSTEM.getDistanceFromMeters(sample.elevation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sampleGetsEntry(WorkoutSample sample, Entry entry) {
|
||||
sample.tmpHeightEntry= entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return getString(R.string.height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "min - " + UnitUtils.CHOSEN_SYSTEM.getShortDistanceUnit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean compare(WorkoutSample sample, Entry entry) {
|
||||
return sample.tmpHeightEntry.equalTo(entry);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void addSpeedDiagram(){
|
||||
addDiagram(new SampleConverter() {
|
||||
@Override
|
||||
public void onCreate() {
|
||||
WorkoutManager.roundSpeedValues(samples);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getValue(WorkoutSample sample) {
|
||||
return (float)UnitUtils.CHOSEN_SYSTEM.getSpeedFromMeterPerSecond(sample.tmpRoundedSpeed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sampleGetsEntry(WorkoutSample sample, Entry entry) {
|
||||
sample.tmpSpeedEntry= entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return getString(R.string.workoutSpeed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "min - " + UnitUtils.CHOSEN_SYSTEM.getSpeedUnit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean compare(WorkoutSample sample, Entry entry) {
|
||||
return sample.tmpSpeedEntry.equalTo(entry);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
WorkoutSample findSample(SampleConverter converter, Entry entry){
|
||||
for(WorkoutSample sample : samples){
|
||||
if(sample.tmpEntry.equalTo(entry)){
|
||||
if(converter.compare(sample, entry)){
|
||||
return sample;
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ package de.tadris.fitness.data;
|
||||
import androidx.room.Database;
|
||||
import androidx.room.RoomDatabase;
|
||||
|
||||
@Database(version = 1, entities = {Workout.class, WorkoutSample.class})
|
||||
@Database(version = 2, entities = {Workout.class, WorkoutSample.class})
|
||||
public abstract class AppDatabase extends RoomDatabase {
|
||||
public abstract WorkoutDao workoutDao();
|
||||
}
|
||||
|
||||
@ -22,10 +22,13 @@ package de.tadris.fitness.data;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.PrimaryKey;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
@Entity(tableName = "workout")
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class Workout{
|
||||
|
||||
public static final String WORKOUT_TYPE_RUNNING= "running";
|
||||
@ -66,6 +69,11 @@ public class Workout{
|
||||
|
||||
public String workoutType;
|
||||
|
||||
|
||||
public float ascent;
|
||||
|
||||
public float descent;
|
||||
|
||||
public int calorie;
|
||||
|
||||
public String toString(){
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
package de.tadris.fitness.data;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.SensorManager;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.List;
|
||||
@ -61,10 +62,14 @@ public class WorkoutManager {
|
||||
// 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;
|
||||
}
|
||||
@ -72,6 +77,36 @@ public class WorkoutManager {
|
||||
|
||||
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
|
||||
|
||||
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]));
|
||||
@ -91,4 +126,15 @@ public class WorkoutManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static void calculateInclination(List<WorkoutSample> samples){
|
||||
samples.get(0).tmpInclination= 0;
|
||||
for(int i= 1; i < samples.size(); i++){
|
||||
WorkoutSample sample= samples.get(i);
|
||||
WorkoutSample lastSample= samples.get(i);
|
||||
double elevationDifference= sample.elevation - sample.elevation;
|
||||
double distance= sample.toLatLong().sphericalDistance(lastSample.toLatLong());
|
||||
sample.tmpInclination= (float)(elevationDifference*100/distance);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ import androidx.room.Ignore;
|
||||
import androidx.room.PrimaryKey;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.github.mikephil.charting.data.Entry;
|
||||
|
||||
import org.mapsforge.core.model.LatLong;
|
||||
@ -38,6 +39,7 @@ import static androidx.room.ForeignKey.CASCADE;
|
||||
parentColumns = "id",
|
||||
childColumns = "workout_id",
|
||||
onDelete = CASCADE))
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class WorkoutSample{
|
||||
|
||||
@PrimaryKey
|
||||
@ -56,18 +58,28 @@ public class WorkoutSample{
|
||||
|
||||
public double elevation;
|
||||
|
||||
public double relativeElevation;
|
||||
|
||||
public double speed;
|
||||
|
||||
@JsonIgnore
|
||||
@Ignore
|
||||
public Entry tmpEntry;
|
||||
public Entry tmpHeightEntry;
|
||||
|
||||
@JsonIgnore
|
||||
@Ignore
|
||||
public Entry tmpSpeedEntry;
|
||||
|
||||
@JsonIgnore
|
||||
@Ignore
|
||||
public double tmpRoundedSpeed;
|
||||
|
||||
@JsonIgnore
|
||||
@Ignore
|
||||
public float tmpPressure;
|
||||
|
||||
@JsonIgnore
|
||||
@Ignore
|
||||
public float tmpInclination;
|
||||
|
||||
public LatLong toLatLong(){
|
||||
return new LatLong(lat, lon);
|
||||
}
|
||||
|
||||
@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019 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.location;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import org.mapsforge.core.graphics.Canvas;
|
||||
import org.mapsforge.core.graphics.Paint;
|
||||
import org.mapsforge.core.graphics.Style;
|
||||
import org.mapsforge.core.model.BoundingBox;
|
||||
import org.mapsforge.core.model.LatLong;
|
||||
import org.mapsforge.core.model.Point;
|
||||
import org.mapsforge.map.android.graphics.AndroidGraphicFactory;
|
||||
import org.mapsforge.map.layer.Layer;
|
||||
import org.mapsforge.map.layer.overlay.Circle;
|
||||
import org.mapsforge.map.layer.overlay.Marker;
|
||||
|
||||
public class MyLocationOverlay extends Layer {
|
||||
|
||||
private final Circle circle;
|
||||
private final Marker marker;
|
||||
private final LocationListener locationListener;
|
||||
|
||||
private static Paint getDefaultFixedPixelCircleFill() {
|
||||
return getPaint(AndroidGraphicFactory.INSTANCE.createColor(255, 0, 0, 255), 0, Style.FILL);
|
||||
}
|
||||
|
||||
private static Paint getDefaultOuterFixedPixelCircleFill(){
|
||||
return getPaint(AndroidGraphicFactory.INSTANCE.createColor(30, 30, 30, 255), 0, Style.FILL);
|
||||
}
|
||||
|
||||
private static Paint getDefaultFixedPixelCircleStroke() {
|
||||
return getPaint(AndroidGraphicFactory.INSTANCE.createColor(255, 255, 255, 255), 7, Style.STROKE);
|
||||
}
|
||||
|
||||
private static Paint getPaint(int color, int strokeWidth, Style style) {
|
||||
Paint paint = AndroidGraphicFactory.INSTANCE.createPaint();
|
||||
paint.setColor(color);
|
||||
paint.setStrokeWidth(strokeWidth);
|
||||
paint.setStyle(style);
|
||||
return paint;
|
||||
}
|
||||
|
||||
public MyLocationOverlay(LocationListener locationListener, Drawable icon) {
|
||||
this.locationListener= locationListener;
|
||||
this.circle= new Circle(null, 0f, getDefaultFixedPixelCircleFill(), null);
|
||||
this.marker= new Marker(null, AndroidGraphicFactory.convertToBitmap(icon), 26, 26);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void draw(BoundingBox boundingBox, byte zoomLevel, Canvas canvas, Point topLeftPoint) {
|
||||
if (this.circle != null) {
|
||||
this.circle.draw(boundingBox, zoomLevel, canvas, topLeftPoint);
|
||||
}
|
||||
this.marker.draw(boundingBox, zoomLevel, canvas, topLeftPoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAdd() {
|
||||
this.circle.setDisplayModel(this.displayModel);
|
||||
this.marker.setDisplayModel(this.displayModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
this.marker.onDestroy();
|
||||
}
|
||||
|
||||
public void setPosition(double latitude, double longitude, float accuracy) {
|
||||
synchronized (this) {
|
||||
LatLong latLong = new LatLong(latitude, longitude);
|
||||
this.marker.setLatLong(latLong);
|
||||
if (this.circle != null) {
|
||||
this.circle.setLatLong(latLong);
|
||||
this.circle.setRadius(accuracy);
|
||||
}
|
||||
requestRedraw();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -18,7 +18,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.tadris.fitness.location;
|
||||
package de.tadris.fitness.recording;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.Service;
|
||||
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2019 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.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import de.tadris.fitness.Instance;
|
||||
|
||||
public class PressureService extends Service {
|
||||
|
||||
private static final String TAG = "PressureService";
|
||||
|
||||
private SensorManager sensorManager;
|
||||
private Instance instance;
|
||||
private Sensor pressureSensor;
|
||||
private PressureListener pressureListener;
|
||||
|
||||
private class PressureListener implements SensorEventListener {
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
instance.lastPressure= event.values[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) { }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
Log.d(TAG, "onCreate");
|
||||
super.onCreate();
|
||||
instance= Instance.getInstance(this);
|
||||
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
|
||||
|
||||
pressureSensor= sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);
|
||||
|
||||
pressureListener= new PressureListener();
|
||||
|
||||
if (pressureSensor != null){
|
||||
instance.pressureAvailable= true;
|
||||
sensorManager.registerListener(pressureListener, pressureSensor, SensorManager.SENSOR_DELAY_NORMAL);
|
||||
} else {
|
||||
instance.pressureAvailable= false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
Log.d(TAG, "onStartCommand");
|
||||
return super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
Log.d(TAG, "onDestroy");
|
||||
super.onDestroy();
|
||||
sensorManager.unregisterListener(pressureListener);
|
||||
}
|
||||
|
||||
@Nullable @Override public IBinder onBind(Intent intent) { return null; }
|
||||
}
|
||||
@ -17,7 +17,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.tadris.fitness.location;
|
||||
package de.tadris.fitness.recording;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
@ -224,10 +224,14 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
|
||||
sample.lat= location.getLatitude();
|
||||
sample.lon= location.getLongitude();
|
||||
sample.elevation= location.getAltitude();
|
||||
sample.relativeElevation= 0.0;
|
||||
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);
|
||||
}
|
||||
@ -282,6 +286,10 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
|
||||
workout.comment= comment;
|
||||
}
|
||||
|
||||
public boolean isPaused(){
|
||||
return state == RecordingState.PAUSED;
|
||||
}
|
||||
|
||||
|
||||
enum RecordingState{
|
||||
IDLE, RUNNING, PAUSED, STOPPED
|
||||
@ -48,14 +48,10 @@ public class CalorieCalculator {
|
||||
*/
|
||||
public static double getMET(Workout workout){
|
||||
double speedInKmh= workout.avgSpeed * 3.6;
|
||||
if(workout.workoutType.equals(Workout.WORKOUT_TYPE_RUNNING)){
|
||||
if(workout.workoutType.equals(Workout.WORKOUT_TYPE_RUNNING) || workout.workoutType.equals(Workout.WORKOUT_TYPE_HIKING)){
|
||||
// This is a linear graph based on the website linked above
|
||||
return Math.max(1.5, speedInKmh*1.117 - 2.1906);
|
||||
}
|
||||
if(workout.workoutType.equals(Workout.WORKOUT_TYPE_HIKING)){
|
||||
// Use fixed MET because no more precise calculation was found
|
||||
return 6.0;
|
||||
}
|
||||
if(workout.workoutType.equals(Workout.WORKOUT_TYPE_CYCLING)){
|
||||
// This is a linear graph based on the website linked above
|
||||
return Math.max(3, (speedInKmh-10) / 1.5);
|
||||
|
||||
@ -24,6 +24,7 @@ import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||
|
||||
import java.io.File;
|
||||
@ -78,6 +79,7 @@ public class Exporter {
|
||||
public static void importData(Context context, Uri input, ExportStatusListener listener) throws IOException{
|
||||
listener.onStatusChanged(0, context.getString(R.string.loadingFile));
|
||||
XmlMapper xmlMapper = new XmlMapper();
|
||||
xmlMapper.configure(JsonParser.Feature.IGNORE_UNDEFINED, true);
|
||||
FitoTrackDataContainer container = xmlMapper.readValue(context.getContentResolver().openInputStream(input), FitoTrackDataContainer.class);
|
||||
|
||||
if(container.version != 1){
|
||||
@ -89,6 +91,7 @@ public class Exporter {
|
||||
.edit().clear()
|
||||
.putInt("weight", container.settings.weight)
|
||||
.putString("unitSystem", container.settings.preferredUnitSystem)
|
||||
.putBoolean("firstStart", false)
|
||||
.commit();
|
||||
|
||||
AppDatabase database= Instance.getInstance(context).db;
|
||||
|
||||
@ -1,5 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2019 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.util.export;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
|
||||
|
||||
import java.util.List;
|
||||
@ -8,6 +28,7 @@ import de.tadris.fitness.data.Workout;
|
||||
import de.tadris.fitness.data.WorkoutSample;
|
||||
|
||||
@JacksonXmlRootElement(localName = "fito-track")
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class FitoTrackDataContainer {
|
||||
|
||||
int version;
|
||||
|
||||
@ -11,13 +11,9 @@ public class ImperialWithMeters extends Imperial {
|
||||
public double getDistanceFromMeters(double meters) {
|
||||
return meters;
|
||||
}
|
||||
@Override
|
||||
public String getLongDistanceUnit() {
|
||||
return "m";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortDistanceUnit() {
|
||||
return "yd";
|
||||
return "m";
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
<string name="errorImportFailed">Der Datenimport ist fehlgeschlagen.</string>
|
||||
<string name="exportAsGpxFile">Als GPX-Datei exportieren</string>
|
||||
<string name="exportData">Daten exportieren</string>
|
||||
<string name="exportDataSummary">"Es wird ein Backup von all deinen Einstallungen und "</string>
|
||||
<string name="exportDataSummary">Erstellt ein Backup von all deinen Einstellungen und deiner Workoutdaten</string>
|
||||
<string name="exporting">Exportieren</string>
|
||||
<string name="finished">Fertig</string>
|
||||
<string name="gps">GPS</string>
|
||||
@ -88,4 +88,10 @@
|
||||
<string name="workoutTypeWalking">Gehen</string>
|
||||
<string name="workout_add">Hinzufügen</string>
|
||||
<string name="workouts">Workouts</string>
|
||||
<string name="enable">Aktivieren</string>
|
||||
<string name="height">Höhe</string>
|
||||
<string name="noGpsTitle">GPS deaktiviert</string>
|
||||
<string name="noGpsMessage">Bitte aktiviere GPS, damit dein Workout aufgezeichnet werden kann.</string>
|
||||
<string name="workoutAscent">Aufstieg</string>
|
||||
<string name="workoutDescent">Abstieg</string>
|
||||
</resources>
|
||||
@ -63,6 +63,12 @@
|
||||
<string name="workoutTotalEnergy">Total Energy</string>
|
||||
<string name="workoutEnergyConsumption">Energy Consumption</string>
|
||||
|
||||
<string name="workoutAscent">Ascent</string>
|
||||
<string name="workoutDescent">Descent</string>
|
||||
|
||||
<string name="height">Height</string>
|
||||
|
||||
|
||||
<string name="workoutTypeRunning">Running</string>
|
||||
<string name="workoutTypeWalking">Walking</string>
|
||||
<string name="workoutTypeJogging">Jogging</string>
|
||||
@ -72,6 +78,10 @@
|
||||
|
||||
<string name="recordWorkout">Record Workout</string>
|
||||
|
||||
<string name="noGpsTitle">GPS disabled</string>
|
||||
<string name="noGpsMessage">Please enable GPS for tracking your workout.</string>
|
||||
<string name="enable">Enable</string>
|
||||
|
||||
<string name="comment">Comment</string>
|
||||
<string name="enterComment">Enter Comment</string>
|
||||
<string name="okay">Okay</string>
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
package de.tadris.fitness;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import de.tadris.fitness.data.Workout;
|
||||
import de.tadris.fitness.util.CalorieCalculator;
|
||||
|
||||
public class CalorieCalculatorTest {
|
||||
|
||||
@Test
|
||||
public void testCalculation(){
|
||||
Workout workout= new Workout();
|
||||
workout.avgSpeed= 2.7d;
|
||||
workout.workoutType= Workout.WORKOUT_TYPE_RUNNING;
|
||||
workout.duration= 1000L * 60 * 10;
|
||||
int calorie= CalorieCalculator.calculateCalories(workout, 80);
|
||||
System.out.println("Calories: " + calorie);
|
||||
Assert.assertEquals(120, calorie, 50);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019 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;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
||||
8
metadata/en-US/changelogs/110.txt
Normal file
8
metadata/en-US/changelogs/110.txt
Normal file
@ -0,0 +1,8 @@
|
||||
New:
|
||||
- Height measurement via GPS and pressure sensor
|
||||
- Show height diagram
|
||||
- Show dialog if GPS is disabled
|
||||
|
||||
Fix:
|
||||
- Back-Button in Settings crashes on earlier Android versions (#20)
|
||||
- Fix Unit system "Imperial with meters"
|
||||
Loading…
x
Reference in New Issue
Block a user