From 7e406db592b02bb74edc786381c1bae195f14cff Mon Sep 17 00:00:00 2001 From: jannis Date: Sat, 17 Aug 2019 20:39:57 +0200 Subject: [PATCH] Show workouts --- app/build.gradle | 1 + app/src/main/AndroidManifest.xml | 7 +- .../main/java/de/tadris/fitness/Instance.java | 9 + .../de/tadris/fitness/WorkoutAdapter.java | 4 +- .../{ => activity}/LauncherActivity.java | 4 +- .../{ => activity}/ListWorkoutsActivity.java | 8 +- .../{ => activity}/RecordWorkoutActivity.java | 31 +-- .../fitness/activity/ShowWorkoutActivity.java | 228 ++++++++++++++++++ .../de/tadris/fitness/data/AppDatabase.java | 2 +- .../java/de/tadris/fitness/data/Workout.java | 15 +- .../tadris/fitness/data/WorkoutManager.java | 23 +- .../fitness/location/WorkoutRecorder.java | 2 +- .../de/tadris/fitness/map/MapManager.java | 54 +++++ .../de/tadris/fitness/map/WorkoutLayer.java | 58 +++++ .../fitness/util/CalorieCalculator.java | 74 ++++++ .../de/tadris/fitness/util/ThemeManager.java | 39 +++ .../de/tadris/fitness/util/UnitUtils.java | 21 +- .../fitness/util/WorkoutTypeCalculator.java | 46 ++++ .../res/layout/activity_list_workouts.xml | 22 +- app/src/main/res/layout/activity_main.xml | 22 +- .../res/layout/activity_record_workout.xml | 9 +- .../main/res/layout/activity_show_workout.xml | 33 +++ app/src/main/res/layout/show_entry.xml | 103 ++++++++ app/src/main/res/layout/view_workout.xml | 81 +++++-- app/src/main/res/menu/show_workout_menu.xml | 27 +++ app/src/main/res/values/colors.xml | 33 ++- app/src/main/res/values/strings.xml | 1 + app/src/main/res/values/styles.xml | 36 ++- 28 files changed, 911 insertions(+), 82 deletions(-) rename app/src/main/java/de/tadris/fitness/{ => activity}/LauncherActivity.java (94%) rename app/src/main/java/de/tadris/fitness/{ => activity}/ListWorkoutsActivity.java (90%) rename app/src/main/java/de/tadris/fitness/{ => activity}/RecordWorkoutActivity.java (75%) create mode 100644 app/src/main/java/de/tadris/fitness/activity/ShowWorkoutActivity.java create mode 100644 app/src/main/java/de/tadris/fitness/map/MapManager.java create mode 100644 app/src/main/java/de/tadris/fitness/map/WorkoutLayer.java create mode 100644 app/src/main/java/de/tadris/fitness/util/CalorieCalculator.java create mode 100644 app/src/main/java/de/tadris/fitness/util/ThemeManager.java create mode 100644 app/src/main/java/de/tadris/fitness/util/WorkoutTypeCalculator.java create mode 100644 app/src/main/res/layout/activity_show_workout.xml create mode 100644 app/src/main/res/layout/show_entry.xml create mode 100644 app/src/main/res/menu/show_workout_menu.xml diff --git a/app/build.gradle b/app/build.gradle index 62da33b..86b19a8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -63,4 +63,5 @@ dependencies { implementation 'androidx.recyclerview:recyclerview:1.0.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1a01d55..81062b7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -32,9 +32,10 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> - - - + + + + diff --git a/app/src/main/java/de/tadris/fitness/Instance.java b/app/src/main/java/de/tadris/fitness/Instance.java index 5b3e6b7..25c8edd 100644 --- a/app/src/main/java/de/tadris/fitness/Instance.java +++ b/app/src/main/java/de/tadris/fitness/Instance.java @@ -21,7 +21,10 @@ 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 de.tadris.fitness.data.AppDatabase; import de.tadris.fitness.location.LocationListener; @@ -45,6 +48,12 @@ public class Instance { private Instance(Context context) { db = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, DATABASE_NAME) .allowMainThreadQueries() + .addMigrations(new Migration(2, 3) { + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + database.execSQL("ALTER TABLE workout add topSpeed DOUBLE not null default 0.0"); + } + }) .build(); locationListener= new LocationListener(context); } diff --git a/app/src/main/java/de/tadris/fitness/WorkoutAdapter.java b/app/src/main/java/de/tadris/fitness/WorkoutAdapter.java index e1fc4df..0d96d45 100644 --- a/app/src/main/java/de/tadris/fitness/WorkoutAdapter.java +++ b/app/src/main/java/de/tadris/fitness/WorkoutAdapter.java @@ -65,7 +65,7 @@ public class WorkoutAdapter extends RecyclerView.Adapter. */ -package de.tadris.fitness; +package de.tadris.fitness.activity; import android.app.Activity; import android.content.Intent; import android.os.Bundle; +import de.tadris.fitness.R; + public class LauncherActivity extends Activity { @Override diff --git a/app/src/main/java/de/tadris/fitness/ListWorkoutsActivity.java b/app/src/main/java/de/tadris/fitness/activity/ListWorkoutsActivity.java similarity index 90% rename from app/src/main/java/de/tadris/fitness/ListWorkoutsActivity.java rename to app/src/main/java/de/tadris/fitness/activity/ListWorkoutsActivity.java index 3e7c892..dd39942 100644 --- a/app/src/main/java/de/tadris/fitness/ListWorkoutsActivity.java +++ b/app/src/main/java/de/tadris/fitness/activity/ListWorkoutsActivity.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package de.tadris.fitness; +package de.tadris.fitness.activity; import android.app.Activity; import android.content.Intent; @@ -28,6 +28,9 @@ import android.view.MenuItem; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import de.tadris.fitness.Instance; +import de.tadris.fitness.R; +import de.tadris.fitness.WorkoutAdapter; import de.tadris.fitness.data.Workout; public class ListWorkoutsActivity extends Activity implements WorkoutAdapter.WorkoutAdapterListener { @@ -62,7 +65,8 @@ public class ListWorkoutsActivity extends Activity implements WorkoutAdapter.Wor @Override public void onItemClick(Workout workout) { - // TODO: open detail View + ShowWorkoutActivity.selectedWorkout= workout; + startActivity(new Intent(this, ShowWorkoutActivity.class)); } @Override diff --git a/app/src/main/java/de/tadris/fitness/RecordWorkoutActivity.java b/app/src/main/java/de/tadris/fitness/activity/RecordWorkoutActivity.java similarity index 75% rename from app/src/main/java/de/tadris/fitness/RecordWorkoutActivity.java rename to app/src/main/java/de/tadris/fitness/activity/RecordWorkoutActivity.java index 147e0e7..7e4cd49 100644 --- a/app/src/main/java/de/tadris/fitness/RecordWorkoutActivity.java +++ b/app/src/main/java/de/tadris/fitness/activity/RecordWorkoutActivity.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package de.tadris.fitness; +package de.tadris.fitness.activity; import android.Manifest; import android.app.Activity; @@ -30,23 +30,20 @@ import android.view.ViewGroup; import androidx.core.app.ActivityCompat; -import org.mapsforge.core.model.LatLong; import org.mapsforge.map.android.graphics.AndroidGraphicFactory; -import org.mapsforge.map.android.util.AndroidUtil; import org.mapsforge.map.android.view.MapView; -import org.mapsforge.map.layer.cache.TileCache; import org.mapsforge.map.layer.download.TileDownloadLayer; +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.MyLocationOverlay; import de.tadris.fitness.location.WorkoutRecorder; -import de.tadris.fitness.map.HumanitarianTileSource; +import de.tadris.fitness.map.MapManager; public class RecordWorkoutActivity extends Activity implements LocationListener.LocationChangeListener { MapView mapView; - MyLocationOverlay locationOverlay; TileDownloadLayer downloadLayer; WorkoutRecorder recorder; @@ -57,24 +54,7 @@ public class RecordWorkoutActivity extends Activity implements LocationListener. this.mapView= new MapView(this); - mapView.setZoomLevelMin((byte) 18); - mapView.setZoomLevelMax((byte) 18); - mapView.setBuiltInZoomControls(false); - - TileCache tileCache = AndroidUtil.createTileCache(this, "mapcache", mapView.getModel().displayModel.getTileSize(), 1f, this.mapView.getModel().frameBufferModel.getOverdrawFactor(), true); - - HumanitarianTileSource tileSource = HumanitarianTileSource.INSTANCE; - tileSource.setUserAgent("mapsforge-android"); - downloadLayer = new TileDownloadLayer(tileCache, mapView.getModel().mapViewPosition, tileSource, AndroidGraphicFactory.INSTANCE); - - mapView.getLayerManager().getLayers().add(downloadLayer); - - locationOverlay= new MyLocationOverlay(Instance.getInstance(this).locationListener, getDrawable(R.drawable.location_marker)); - - mapView.getLayerManager().redrawLayers(); - - mapView.setZoomLevel((byte) 18); - mapView.setCenter(new LatLong(52, 13)); + downloadLayer= MapManager.setupMap(mapView); ((ViewGroup)findViewById(R.id.recordMapViewrRoot)).addView(mapView); @@ -113,7 +93,6 @@ public class RecordWorkoutActivity extends Activity implements LocationListener. @Override public void onLocationChange(Location location) { mapView.getModel().mapViewPosition.animateTo(LocationListener.locationToLatLong(location)); - locationOverlay.setPosition(location.getLatitude(), location.getLongitude(), location.getAccuracy()); } @Override diff --git a/app/src/main/java/de/tadris/fitness/activity/ShowWorkoutActivity.java b/app/src/main/java/de/tadris/fitness/activity/ShowWorkoutActivity.java new file mode 100644 index 0000000..d121a47 --- /dev/null +++ b/app/src/main/java/de/tadris/fitness/activity/ShowWorkoutActivity.java @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2019 Jannis Scheibe + * + * 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 . + */ + +package de.tadris.fitness.activity; + +import android.app.Activity; +import android.content.res.Resources; +import android.graphics.Typeface; +import android.os.Bundle; +import android.os.Handler; +import android.util.TypedValue; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import org.mapsforge.core.model.BoundingBox; +import org.mapsforge.core.model.MapPosition; +import org.mapsforge.core.util.LatLongUtils; +import org.mapsforge.map.android.graphics.AndroidGraphicFactory; +import org.mapsforge.map.android.view.MapView; +import org.mapsforge.map.layer.download.TileDownloadLayer; + +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import de.tadris.fitness.Instance; +import de.tadris.fitness.R; +import de.tadris.fitness.data.Workout; +import de.tadris.fitness.data.WorkoutSample; +import de.tadris.fitness.map.MapManager; +import de.tadris.fitness.map.WorkoutLayer; +import de.tadris.fitness.util.ThemeManager; +import de.tadris.fitness.util.UnitUtils; + +public class ShowWorkoutActivity extends Activity { + static Workout selectedWorkout; + + List samples; + Workout workout; + ViewGroup root; + Resources.Theme theme; + MapView map; + TileDownloadLayer downloadLayer; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + workout= selectedWorkout; + samples= Arrays.asList(Instance.getInstance(this).db.workoutDao().getAllSamplesOfWorkout(workout.id)); + setTheme(ThemeManager.getThemeByWorkout(workout, this)); + setContentView(R.layout.activity_show_workout); + + getActionBar().setDisplayHomeAsUpEnabled(true); + + theme= getTheme(); + + root= findViewById(R.id.showWorkoutRoot); + + addTitle("Zeit"); + addKeyValue("Datum", getDate(), "Dauer", UnitUtils.getHourMinuteTime(workout.getDuration())); + addKeyValue("Startzeit", SimpleDateFormat.getTimeInstance().format(new Date(workout.start)), + "Endzeit", SimpleDateFormat.getTimeInstance().format(new Date(workout.end))); + + addKeyValue("Distanz", UnitUtils.getDistance(workout.length), "Pace", UnitUtils.round(workout.avgPace, 1) + " min/km"); + + addTitle("Geschwindigkeit"); + + addKeyValue("Durchschnittsgeschw.", UnitUtils.getSpeed(workout.avgSpeed), + "Top Geschw.", UnitUtils.round(workout.topSpeed, 1) + " km/h"); + + // TODO: add speed diagram + + addTitle("Verbrauchte Energie"); + addKeyValue("Gesamtverbrauch", workout.calorie + " kcal", + "Relativverbrauch", UnitUtils.round(((double)workout.calorie / workout.length / 1000), 2) + " kcal/km"); + + addTitle("Route"); + + addMap(); + + } + + String getDate(){ + return SimpleDateFormat.getDateInstance().format(new Date(workout.start)); + } + + + void addTitle(String title){ + TextView textView= new TextView(this); + textView.setText(title); + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20); + textView.setTextColor(getThemePrimaryColor()); + textView.setTypeface(Typeface.DEFAULT_BOLD); + textView.setAllCaps(true); + + root.addView(textView); + } + + void addKeyValue(String key1, String value1){ + addKeyValue(key1, value1, "", ""); + } + + void addKeyValue(String key1, String value1, String key2, String value2){ + View v= getLayoutInflater().inflate(R.layout.show_entry, root, false); + + TextView title1= v.findViewById(R.id.v1title); + TextView title2= v.findViewById(R.id.v2title); + TextView v1= v.findViewById(R.id.v1value); + TextView v2= v.findViewById(R.id.v2value); + + title1.setText(key1); + title2.setText(key2); + v1.setText(value1); + v2.setText(value2); + + root.addView(v); + } + + void addDiagram(){ + + } + + void addMap(){ + map= new MapView(this); + downloadLayer= MapManager.setupMap(map); + map.setZoomLevelMin((byte)2); + map.setZoomLevelMax((byte)18); + + WorkoutLayer workoutLayer= new WorkoutLayer(samples, getThemePrimaryColor()); + map.addLayer(workoutLayer); + + final BoundingBox bounds= new BoundingBox(workoutLayer.getLatLongs()).extendMeters(50); + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + map.getModel().mapViewPosition.setMapPosition(new MapPosition(bounds.getCenterPoint(), + (byte)(LatLongUtils.zoomForBounds(map.getDimension(), bounds, map.getModel().displayModel.getTileSize())))); + map.animate().alpha(1f).setDuration(1000).start(); + } + }, 1000); + + map.getModel().mapViewPosition.setMapLimit(bounds); + + + root.addView(map, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getWindowManager().getDefaultDisplay().getWidth()*3/4)); + map.setAlpha(0); + } + + private int getThemePrimaryColor() { + final TypedValue value = new TypedValue (); + getTheme().resolveAttribute (android.R.attr.colorPrimary, value, true); + return value.data; + } + + @Override + protected void onDestroy() { + map.destroyAll(); + AndroidGraphicFactory.clearResourceMemoryCache(); + super.onDestroy(); + } + + @Override + public void onPause(){ + super.onPause(); + downloadLayer.onPause(); + } + + public void onResume(){ + super.onResume(); + downloadLayer.onResume(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.show_workout_menu, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + if(id == R.id.actionDeleteWorkout){ + // TODO: delete workout + return true; + }else if(id == android.R.id.home){ + finish(); + return true; + } + return super.onOptionsItemSelected(item); + } + + /*private int zoomToBounds(BoundingBox boundingBox) { + int TILE_SIZE= map.getModel().displayModel.getTileSize(); + Dimension mapViewDimension = map.getModel().mapViewDimension.getDimension(); + if(mapViewDimension == null) + return 0; + double dxMax = longitudeToPixelX(boundingBox.maxLongitude, (byte) 0) / TILE_SIZE; + double dxMin = longitudeToPixelX(boundingBox.minLongitude, (byte) 0) / TILE_SIZE; + double zoomX = floor(-log(3.8) * log(abs(dxMax-dxMin)) + mapViewDimension.width / TILE_SIZE); + double dyMax = latitudeToPixelY(boundingBox.maxLatitude, (byte) 0) / TILE_SIZE; + double dyMin = latitudeToPixelY(boundingBox.minLatitude, (byte) 0) / TILE_SIZE; + double zoomY = floor(-log(3.8) * log(abs(dyMax-dyMin)) + mapViewDimension.height / TILE_SIZE); + return Double.valueOf(min(zoomX, zoomY)).intValue(); + }*/ + +} diff --git a/app/src/main/java/de/tadris/fitness/data/AppDatabase.java b/app/src/main/java/de/tadris/fitness/data/AppDatabase.java index cd1fc75..e1ce9ed 100644 --- a/app/src/main/java/de/tadris/fitness/data/AppDatabase.java +++ b/app/src/main/java/de/tadris/fitness/data/AppDatabase.java @@ -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 = 3, entities = {Workout.class, WorkoutSample.class}) public abstract class AppDatabase extends RoomDatabase { public abstract WorkoutDao workoutDao(); } diff --git a/app/src/main/java/de/tadris/fitness/data/Workout.java b/app/src/main/java/de/tadris/fitness/data/Workout.java index d553397..4c87a23 100644 --- a/app/src/main/java/de/tadris/fitness/data/Workout.java +++ b/app/src/main/java/de/tadris/fitness/data/Workout.java @@ -26,6 +26,8 @@ import androidx.room.PrimaryKey; public class Workout{ public static final String WORKOUT_TYPE_RUNNING= "running"; + public static final String WORKOUT_TYPE_WALKING= "running"; + public static final String WORKOUT_TYPE_HIKING= "hiking"; public static final String WORKOUT_TYPE_CYCLING= "cycling"; @PrimaryKey @@ -44,6 +46,11 @@ public class Workout{ */ public double avgSpeed; + /** + * Top speed in m/s + */ + public double topSpeed; + /** * Average pace of workout in km/min */ @@ -51,11 +58,9 @@ public class Workout{ public String workoutType; - /** - * Gets time of workout - * @return time in milliseconds - */ - public long getTime(){ + public int calorie; + + public long getDuration(){ return end - start; } diff --git a/app/src/main/java/de/tadris/fitness/data/WorkoutManager.java b/app/src/main/java/de/tadris/fitness/data/WorkoutManager.java index 7ae63f6..1f1e83f 100644 --- a/app/src/main/java/de/tadris/fitness/data/WorkoutManager.java +++ b/app/src/main/java/de/tadris/fitness/data/WorkoutManager.java @@ -19,13 +19,19 @@ package de.tadris.fitness.data; +import android.content.Context; + import java.util.List; import de.tadris.fitness.Instance; +import de.tadris.fitness.util.CalorieCalculator; public class WorkoutManager { - public static void insertWorkout(Instance instance, Workout workout, List samples){ + public static void insertWorkout(Context context, Workout workout, List samples){ + AppDatabase db= Instance.getInstance(context).db; + + workout.id= System.currentTimeMillis(); // Calculating values @@ -34,19 +40,28 @@ public class WorkoutManager { length+= samples.get(i - 1).toLatLong().sphericalDistance(samples.get(i).toLatLong()); } workout.length= (int)length; - workout.avgSpeed= ((double) workout.length / 1000) / ((double) workout.getTime() / 1000); - workout.avgPace= (double)(workout.getTime() / 1000 / 60) / ((double) workout.length / 1000); + workout.avgSpeed= ((double) workout.length) / ((double) workout.getDuration() / 1000); + workout.avgPace= (double)(workout.getDuration() / 1000 / 60) / ((double) workout.length / 1000); + workout.calorie= CalorieCalculator.calculateCalories(workout, 80); + // TODO: use user weight // Setting workoutId in the samples int i= 0; + double topSpeed= 0; for(WorkoutSample sample : samples){ i++; sample.id= workout.id + i; sample.workoutId= workout.id; + if(sample.speed > topSpeed){ + topSpeed= sample.speed; + } } + workout.topSpeed= topSpeed; + + // Saving workout and samples - instance.db.workoutDao().insertWorkoutAndSamples(workout, samples.toArray(new WorkoutSample[0])); + db.workoutDao().insertWorkoutAndSamples(workout, samples.toArray(new WorkoutSample[0])); } diff --git a/app/src/main/java/de/tadris/fitness/location/WorkoutRecorder.java b/app/src/main/java/de/tadris/fitness/location/WorkoutRecorder.java index 23de5a9..8822822 100644 --- a/app/src/main/java/de/tadris/fitness/location/WorkoutRecorder.java +++ b/app/src/main/java/de/tadris/fitness/location/WorkoutRecorder.java @@ -88,7 +88,7 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener if(state != RecordingState.STOPPED){ throw new IllegalStateException("Cannot save recording, recorder was not stopped. state = " + state); } - WorkoutManager.insertWorkout(Instance.getInstance(context), workout, samples); + WorkoutManager.insertWorkout(context, workout, samples); } public int getSampleCount(){ diff --git a/app/src/main/java/de/tadris/fitness/map/MapManager.java b/app/src/main/java/de/tadris/fitness/map/MapManager.java new file mode 100644 index 0000000..367e8da --- /dev/null +++ b/app/src/main/java/de/tadris/fitness/map/MapManager.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019 Jannis Scheibe + * + * 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 . + */ + +package de.tadris.fitness.map; + +import org.mapsforge.core.model.LatLong; +import org.mapsforge.map.android.graphics.AndroidGraphicFactory; +import org.mapsforge.map.android.util.AndroidUtil; +import org.mapsforge.map.android.view.MapView; +import org.mapsforge.map.layer.cache.TileCache; +import org.mapsforge.map.layer.download.TileDownloadLayer; + +public class MapManager { + + public static TileDownloadLayer setupMap(MapView mapView){ + mapView.setZoomLevelMin((byte) 18); + mapView.setZoomLevelMax((byte) 18); + mapView.setBuiltInZoomControls(false); + + TileCache tileCache = AndroidUtil.createTileCache(mapView.getContext(), "mapcache", + mapView.getModel().displayModel.getTileSize(), 1f, + mapView.getModel().frameBufferModel.getOverdrawFactor(), true); + + HumanitarianTileSource tileSource = HumanitarianTileSource.INSTANCE; + tileSource.setUserAgent("mapsforge-android"); + TileDownloadLayer downloadLayer = new TileDownloadLayer(tileCache, mapView.getModel().mapViewPosition, tileSource, AndroidGraphicFactory.INSTANCE); + + mapView.getLayerManager().getLayers().add(downloadLayer); + + mapView.getLayerManager().redrawLayers(); + + mapView.setZoomLevel((byte) 18); + mapView.setCenter(new LatLong(52, 13)); + + return downloadLayer; + } + +} diff --git a/app/src/main/java/de/tadris/fitness/map/WorkoutLayer.java b/app/src/main/java/de/tadris/fitness/map/WorkoutLayer.java new file mode 100644 index 0000000..8afa936 --- /dev/null +++ b/app/src/main/java/de/tadris/fitness/map/WorkoutLayer.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019 Jannis Scheibe + * + * 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 . + */ + +package de.tadris.fitness.map; + +import org.mapsforge.core.graphics.Paint; +import org.mapsforge.core.graphics.Style; +import org.mapsforge.map.android.graphics.AndroidGraphicFactory; +import org.mapsforge.map.layer.overlay.Polyline; + +import java.util.List; + +import de.tadris.fitness.data.WorkoutSample; + +public class WorkoutLayer extends Polyline { + + public static Paint getDEFAULT_PAINT_STROKE(int color){ + Paint paint= AndroidGraphicFactory.INSTANCE.createPaint(); + paint.setStyle(Style.STROKE); + paint.setColor(color); + paint.setStrokeWidth(14f); + return paint; + } + + List samples; + + public WorkoutLayer(List samples, int color) { + this(getDEFAULT_PAINT_STROKE(color), samples); + } + + public WorkoutLayer(Paint paintStroke, List samples) { + super(paintStroke, AndroidGraphicFactory.INSTANCE); + this.samples = samples; + init(); + } + + private void init(){ + for(WorkoutSample sample : samples){ + addPoint(sample.toLatLong()); + } + } +} diff --git a/app/src/main/java/de/tadris/fitness/util/CalorieCalculator.java b/app/src/main/java/de/tadris/fitness/util/CalorieCalculator.java new file mode 100644 index 0000000..5f50d92 --- /dev/null +++ b/app/src/main/java/de/tadris/fitness/util/CalorieCalculator.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2019 Jannis Scheibe + * + * 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 . + */ + +package de.tadris.fitness.util; + +import de.tadris.fitness.data.Workout; + +public class CalorieCalculator { + + /** + * + * @param workout the workout + * @param weight the weight of the person in kilogram + * @return calories burned + */ + public static int calculateCalories(Workout workout, double weight){ + int mins= (int)(workout.getDuration() / 1000 / 60); + return (int)(mins * (getMET(workout) * 3.5 * weight) / 200); + } + + /** + * calorie calculation based on @link { https://www.topendsports.com/weight-loss/energy-met.htm } + * + * @param workout + * @return MET + */ + public static double getMET(Workout workout){ + if(workout.workoutType.equals(Workout.WORKOUT_TYPE_RUNNING)){ + if(workout.avgSpeed < 3.2){ + return 1.5; + }else if(workout.avgSpeed < 4.0){ + return 2.5; + }else if(workout.avgSpeed < 4.8){ + return 3.25; + }else if(workout.avgSpeed < 5.9){ + return 4.0; + }else if(workout.avgSpeed < 7.0){ + return 5.0; + }else if(workout.avgSpeed < 8.0){ + return 7.0; + }else if(workout.avgSpeed < 9.6){ + return 9.0; + }else if(workout.avgSpeed < 12.8){ + return 12.0; + }else{ + return 16; + } + } + if(workout.workoutType.equals(Workout.WORKOUT_TYPE_HIKING)){ + return 6.3; + } + if(workout.workoutType.equals(Workout.WORKOUT_TYPE_CYCLING)){ + return Math.max(3, (workout.avgSpeed-10) / 1.5); + } + return -1; + } + +} diff --git a/app/src/main/java/de/tadris/fitness/util/ThemeManager.java b/app/src/main/java/de/tadris/fitness/util/ThemeManager.java new file mode 100644 index 0000000..5510c22 --- /dev/null +++ b/app/src/main/java/de/tadris/fitness/util/ThemeManager.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 Jannis Scheibe + * + * 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 . + */ + +package de.tadris.fitness.util; + +import android.content.Context; + +import de.tadris.fitness.R; +import de.tadris.fitness.data.Workout; + +public class ThemeManager { + + + public static int getThemeByWorkout(Workout workout, Context context){ + switch (workout.workoutType){ + case Workout.WORKOUT_TYPE_RUNNING: return R.style.Running; + case Workout.WORKOUT_TYPE_CYCLING: return R.style.Bicycling; + default: return R.style.AppTheme; + } + } + +} + diff --git a/app/src/main/java/de/tadris/fitness/util/UnitUtils.java b/app/src/main/java/de/tadris/fitness/util/UnitUtils.java index d13752a..d8a5dad 100644 --- a/app/src/main/java/de/tadris/fitness/util/UnitUtils.java +++ b/app/src/main/java/de/tadris/fitness/util/UnitUtils.java @@ -28,9 +28,9 @@ public class UnitUtils { int remainingMinutes= (int)mins % 60; if(hours > 0){ - return hours + "h " + remainingMinutes + "m"; + return hours + " h " + remainingMinutes + " m"; }else{ - return remainingMinutes + "min"; + return remainingMinutes + " min"; } } @@ -40,6 +40,7 @@ public class UnitUtils { * @return String in preferred unit */ public static String getDistance(int distance){ + // TODO: use preferred unit by user if(distance >= 1000){ return getDistanceInKilometers((double)distance); }else{ @@ -47,16 +48,26 @@ public class UnitUtils { } } + /** + * + * @param speed speed in m/s + * @return speed in km/h + */ + public static String getSpeed(double speed){ + // TODO: use preferred unit by user + return round(speed*3.6, 1) + " km/h"; + } + public static String getDistanceInMeters(int distance){ - return distance + "m"; + return distance + " m"; } public static String getDistanceInKilometers(double distance){ - return round(distance / 1000, 1) + "km"; + return round(distance / 1000, 1) + " km"; } public static double round(double d, int count){ - return (double)Math.round(d * Math.pow(10, count)) / count; + return (double)Math.round(d * Math.pow(10, count)) / Math.pow(10, count); } diff --git a/app/src/main/java/de/tadris/fitness/util/WorkoutTypeCalculator.java b/app/src/main/java/de/tadris/fitness/util/WorkoutTypeCalculator.java new file mode 100644 index 0000000..5382925 --- /dev/null +++ b/app/src/main/java/de/tadris/fitness/util/WorkoutTypeCalculator.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019 Jannis Scheibe + * + * 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 . + */ + +package de.tadris.fitness.util; + +import de.tadris.fitness.data.Workout; + +public class WorkoutTypeCalculator { + + public static String getType(Workout workout){ + // TODO: use localisation + if(workout.workoutType.equals(Workout.WORKOUT_TYPE_RUNNING)){ + if(workout.avgSpeed < 7){ + return "Walking"; + }else if(workout.avgSpeed < 9.6){ + return "Jogging"; + }else{ + return "Running"; + } + } + if(workout.workoutType.equals(Workout.WORKOUT_TYPE_CYCLING)){ + return "Cycling"; + } + if(workout.workoutType.equals(Workout.WORKOUT_TYPE_HIKING)){ + return "Hiking"; + } + return "Unknown"; + } + +} diff --git a/app/src/main/res/layout/activity_list_workouts.xml b/app/src/main/res/layout/activity_list_workouts.xml index c43a178..ce1cc41 100644 --- a/app/src/main/res/layout/activity_list_workouts.xml +++ b/app/src/main/res/layout/activity_list_workouts.xml @@ -1,10 +1,28 @@ + + + tools:context=".activity.ListWorkoutsActivity"> + + \ No newline at end of file + tools:context=".activity.LauncherActivity" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_record_workout.xml b/app/src/main/res/layout/activity_record_workout.xml index 6088777..b581b3c 100644 --- a/app/src/main/res/layout/activity_record_workout.xml +++ b/app/src/main/res/layout/activity_record_workout.xml @@ -22,7 +22,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".RecordWorkoutActivity"> + tools:context=".activity.RecordWorkoutActivity"> + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/show_entry.xml b/app/src/main/res/layout/show_entry.xml new file mode 100644 index 0000000..8076af6 --- /dev/null +++ b/app/src/main/res/layout/show_entry.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_workout.xml b/app/src/main/res/layout/view_workout.xml index d4675ae..17e7c86 100644 --- a/app/src/main/res/layout/view_workout.xml +++ b/app/src/main/res/layout/view_workout.xml @@ -1,34 +1,69 @@ + + - - - - - - - + android:orientation="vertical" + android:background="?android:selectableItemBackground"> + android:orientation="horizontal"> + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/show_workout_menu.xml b/app/src/main/res/menu/show_workout_menu.xml new file mode 100644 index 0000000..da4a8a4 --- /dev/null +++ b/app/src/main/res/menu/show_workout_menu.xml @@ -0,0 +1,27 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index ef5e1fc..2df259c 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,7 +1,34 @@ + + - #008577 - #00574B - #D81B60 + #FB8C00 + #EF6C00 + + #43A047 + #2E7D32 + + #523220 + #462A1D + + #4CAF50 #C8000000 + #C8414141 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index eed3063..21d86c7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -21,4 +21,5 @@ FitoTrack Add Stop + Delete diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index e82a4c3..4b3749b 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,11 +1,45 @@ + + - + + + + + +