#53 Manually add workouts

This commit is contained in:
jannis 2020-03-10 21:13:15 +01:00
parent e5f3f9f0f7
commit 3d930cc49e
44 changed files with 1128 additions and 248 deletions

View File

@ -73,7 +73,7 @@ dependencies {
implementation 'org.mapsforge:mapsforge-map-android:0.11.0'
implementation 'com.caverock:androidsvg:1.3'
// Charts
// UI
implementation 'net.sf.kxml:kxml2:2.3.0'
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
implementation 'com.github.clans:fab:1.6.4'

View File

@ -39,7 +39,8 @@
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name=".activity.VoiceAnnouncementsSettingsActivity"></activity>
<activity android:name=".activity.EnterWorkoutActivity"></activity>
<activity android:name=".activity.VoiceAnnouncementsSettingsActivity" />
<activity
android:name=".activity.ShowWorkoutMapActivity"
android:screenOrientation="portrait" />

View File

@ -60,7 +60,7 @@ public class Instance {
.addMigrations(new Migration(1, 2) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
try{
try {
database.beginTransaction();
database.execSQL("ALTER table workout add descent REAL NOT NULL DEFAULT 0;");
@ -85,7 +85,20 @@ public class Instance {
database.execSQL("DROP TABLE workout_sample2");
database.setTransactionSuccessful();
}finally {
} finally {
database.endTransaction();
}
}
}, new Migration(2, 3) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
try {
database.beginTransaction();
database.execSQL("ALTER table workout add COLUMN edited INTEGER not null default 0");
database.setTransactionSuccessful();
} finally {
database.endTransaction();
}
}

View File

@ -0,0 +1,221 @@
/*
* 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.activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.InputType;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import de.tadris.fitness.R;
import de.tadris.fitness.data.WorkoutBuilder;
import de.tadris.fitness.data.WorkoutType;
import de.tadris.fitness.dialog.DatePickerFragment;
import de.tadris.fitness.dialog.DurationPickerDialogFragment;
import de.tadris.fitness.dialog.SelectWorkoutTypeDialog;
import de.tadris.fitness.dialog.TimePickerFragment;
import de.tadris.fitness.util.unit.UnitUtils;
public class EnterWorkoutActivity extends InformationActivity implements SelectWorkoutTypeDialog.WorkoutTypeSelectListener, DatePickerFragment.DatePickerCallback, TimePickerFragment.TimePickerCallback, DurationPickerDialogFragment.DurationPickListener {
WorkoutBuilder workoutBuilder = new WorkoutBuilder();
TextView typeTextView, dateTextView, timeTextView, durationTextView;
EditText distanceEditText, commentEditText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_enter_workout);
initRoot();
addTitle(getString(R.string.info));
setupActionBar();
KeyValueLine typeLine = addKeyValueLine(getString(R.string.type));
typeTextView = typeLine.value;
typeLine.lineRoot.setOnClickListener(v -> showTypeSelection());
distanceEditText = new EditText(this);
distanceEditText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
distanceEditText.setSingleLine(true);
distanceEditText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
distanceEditText.setOnEditorActionListener((v, actionId, event) -> {
// If the User clicks on the finish button on the keyboard, continue by showing the date selection
if (actionId == EditorInfo.IME_ACTION_SEARCH ||
actionId == EditorInfo.IME_ACTION_DONE ||
event != null &&
event.getAction() == KeyEvent.ACTION_DOWN &&
event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
if (event == null || !event.isShiftPressed()) {
showDateSelection();
return true;
}
}
return false;
});
addKeyValueLine(getString(R.string.workoutDistance), distanceEditText, UnitUtils.CHOSEN_SYSTEM.getLongDistanceUnit());
KeyValueLine dateLine = addKeyValueLine(getString(R.string.workoutDate));
dateLine.lineRoot.setOnClickListener(v -> showDateSelection());
dateTextView = dateLine.value;
KeyValueLine timeLine = addKeyValueLine(getString(R.string.workoutStartTime));
timeLine.lineRoot.setOnClickListener(v -> showTimeSelection());
timeTextView = timeLine.value;
KeyValueLine durationLine = addKeyValueLine(getString(R.string.workoutDuration));
durationLine.lineRoot.setOnClickListener(v -> showDurationSelection());
durationTextView = durationLine.value;
addTitle(getString(R.string.custom));
commentEditText = new EditText(this);
commentEditText.setSingleLine(true);
root.addView(commentEditText);
updateTextViews();
}
private void saveWorkout() {
workoutBuilder.setComment(commentEditText.getText().toString());
try {
workoutBuilder.setLength((int) (Double.parseDouble(distanceEditText.getText().toString()) * 1000));
} catch (NumberFormatException ignored) {
distanceEditText.requestFocus();
distanceEditText.setError(getString(R.string.errorEnterValidNumber));
return;
}
if (workoutBuilder.getStart().getTimeInMillis() > System.currentTimeMillis()) {
Toast.makeText(this, R.string.errorWorkoutAddFuture, Toast.LENGTH_LONG).show();
return;
}
if (workoutBuilder.getDuration() < 1000) {
Toast.makeText(this, R.string.errorEnterValidDuration, Toast.LENGTH_LONG).show();
return;
}
ShowWorkoutActivity.selectedWorkout = workoutBuilder.insertWorkout(this);
startActivity(new Intent(this, ShowWorkoutActivity.class));
finish();
}
private void updateTextViews() {
typeTextView.setText(getString(workoutBuilder.getWorkoutType().title));
dateTextView.setText(SimpleDateFormat.getDateInstance().format(workoutBuilder.getStart().getTime()));
timeTextView.setText(SimpleDateFormat.getTimeInstance().format(workoutBuilder.getStart().getTime()));
durationTextView.setText(UnitUtils.getHourMinuteTime(workoutBuilder.getDuration()));
}
private void showTypeSelection() {
new SelectWorkoutTypeDialog(this, this).show();
}
@Override
public void onSelectWorkoutType(WorkoutType workoutType) {
workoutBuilder.setWorkoutType(workoutType);
updateTextViews();
distanceEditText.requestFocus();
}
private void showDateSelection() {
DatePickerFragment fragment = new DatePickerFragment();
fragment.callback = this;
fragment.show(getFragmentManager(), "datePicker");
}
@Override
public void onDatePick(int year, int month, int day) {
workoutBuilder.getStart().set(year, month, day);
updateTextViews();
showTimeSelection();
}
private void showTimeSelection() {
TimePickerFragment fragment = new TimePickerFragment();
fragment.callback = this;
fragment.show(getFragmentManager(), "timePicker");
}
@Override
public void onTimePick(int hour, int minute) {
workoutBuilder.getStart().set(Calendar.HOUR_OF_DAY, hour);
workoutBuilder.getStart().set(Calendar.MINUTE, minute);
workoutBuilder.getStart().set(Calendar.SECOND, 0);
updateTextViews();
showDurationSelection();
}
private void showDurationSelection() {
DurationPickerDialogFragment fragment = new DurationPickerDialogFragment(this, this, workoutBuilder.getDuration());
fragment.listener = this;
fragment.initialDuration = workoutBuilder.getDuration();
fragment.show();
}
@Override
public void onDurationPick(long duration) {
workoutBuilder.setDuration(duration);
updateTextViews();
commentEditText.requestFocus();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.enter_workout_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch (id) {
case R.id.actionEnterWorkoutAdd:
saveWorkout();
return true;
case android.R.id.home:
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
private void setupActionBar() {
if (getActionBar() != null) {
getActionBar().setDisplayHomeAsUpEnabled(true);
}
}
@Override
void initRoot() {
root = findViewById(R.id.enterWorkoutRoot);
}
}

View File

@ -0,0 +1,141 @@
/*
* 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.activity;
import android.graphics.Typeface;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import de.tadris.fitness.R;
public abstract class InformationActivity extends FitoTrackActivity {
ViewGroup root;
protected 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);
textView.setPadding(0, 20, 0, 0);
root.addView(textView);
}
protected TextView addText(String text, boolean themeColor) {
TextView textView = createTextView(text, themeColor);
root.addView(textView);
return textView;
}
protected TextView createTextView(String text) {
return createTextView(text, false);
}
protected TextView createTextView(String text, boolean themeColor) {
TextView textView = new TextView(this);
textView.setText(text);
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
if (themeColor) {
textView.setTextColor(getThemePrimaryColor());
} else {
textView.setTextColor(getResources().getColor(R.color.textLighterBlack));
}
textView.setPadding(0, 20, 0, 0);
return textView;
}
protected void addKeyValue(String key1, String value1) {
addKeyValue(key1, value1, "", "");
}
protected 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);
}
protected KeyValueLine addKeyValueLine(String key) {
return addKeyValueLine(key, "");
}
protected KeyValueLine addKeyValueLine(String key, String value) {
return addKeyValueLine(key, null, value);
}
protected KeyValueLine addKeyValueLine(String key, @Nullable View view) {
return addKeyValueLine(key, view, "");
}
protected KeyValueLine addKeyValueLine(String key, @Nullable View view, String value) {
View v = getLayoutInflater().inflate(R.layout.enter_workout_line, root, false);
TextView keyView = v.findViewById(R.id.lineKey);
TextView valueView = v.findViewById(R.id.lineValue);
LinearLayout customViewRoot = v.findViewById(R.id.lineViewRoot);
keyView.setText(key);
valueView.setText(value);
if (view != null) {
customViewRoot.addView(view);
}
root.addView(v);
return new KeyValueLine(v, keyView, valueView, view);
}
public static class KeyValueLine {
public View lineRoot;
public TextView key;
public TextView value;
public View customView;
public KeyValueLine(View lineRoot, TextView key, TextView value, View customView) {
this.lineRoot = lineRoot;
this.key = key;
this.value = value;
this.customView = customView;
}
}
abstract void initRoot();
}

View File

@ -37,6 +37,7 @@ import com.github.clans.fab.FloatingActionMenu;
import de.tadris.fitness.Instance;
import de.tadris.fitness.R;
import de.tadris.fitness.data.Workout;
import de.tadris.fitness.data.WorkoutType;
import de.tadris.fitness.util.DialogUtils;
import de.tadris.fitness.view.WorkoutAdapter;
@ -63,16 +64,17 @@ public class ListWorkoutsActivity extends Activity implements WorkoutAdapter.Wor
menu= findViewById(R.id.workoutListMenu);
menu.setOnMenuButtonLongClickListener(v -> {
if(workouts.length > 0){
startRecording(workouts[0].workoutType);
startRecording(workouts[0].getWorkoutType());
return true;
}else{
return false;
}
});
findViewById(R.id.workoutListRecordRunning).setOnClickListener(v -> startRecording(Workout.WORKOUT_TYPE_RUNNING));
findViewById(R.id.workoutListRecordHiking) .setOnClickListener(v -> startRecording(Workout.WORKOUT_TYPE_HIKING));
findViewById(R.id.workoutListRecordCycling).setOnClickListener(v -> startRecording(Workout.WORKOUT_TYPE_CYCLING));
findViewById(R.id.workoutListRecordRunning).setOnClickListener(v -> startRecording(WorkoutType.RUNNING));
findViewById(R.id.workoutListRecordHiking).setOnClickListener(v -> startRecording(WorkoutType.HIKING));
findViewById(R.id.workoutListRecordCycling).setOnClickListener(v -> startRecording(WorkoutType.CYCLING));
findViewById(R.id.workoutListEnter).setOnClickListener(v -> startEnterWorkoutActivity());
checkFirstStart();
@ -92,7 +94,13 @@ public class ListWorkoutsActivity extends Activity implements WorkoutAdapter.Wor
}
}
private void startRecording(String activity) {
private void startEnterWorkoutActivity() {
menu.close(true);
final Intent intent = new Intent(this, EnterWorkoutActivity.class);
new Handler().postDelayed(() -> startActivity(intent), 300);
}
private void startRecording(WorkoutType activity) {
menu.close(true);
RecordWorkoutActivity.ACTIVITY= activity;
final Intent intent= new Intent(this, RecordWorkoutActivity.class);

View File

@ -53,20 +53,18 @@ import java.util.List;
import de.tadris.fitness.Instance;
import de.tadris.fitness.R;
import de.tadris.fitness.announcement.AnnouncementGPSStatus;
import de.tadris.fitness.announcement.VoiceAnnouncements;
import de.tadris.fitness.data.Workout;
import de.tadris.fitness.data.WorkoutType;
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.recording.announcement.AnnouncementGPSStatus;
import de.tadris.fitness.recording.announcement.VoiceAnnouncements;
import de.tadris.fitness.util.unit.UnitUtils;
public class RecordWorkoutActivity extends FitoTrackActivity implements LocationListener.LocationChangeListener, WorkoutRecorder.WorkoutRecorderListener, VoiceAnnouncements.VoiceAnnouncementCallback {
public static String ACTIVITY= Workout.WORKOUT_TYPE_RUNNING;
public static WorkoutType ACTIVITY = WorkoutType.OTHER;
private MapView mapView;
private TileDownloadLayer downloadLayer;
@ -90,7 +88,7 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(ThemeManager.getThemeByWorkoutType(ACTIVITY));
setTheme(ACTIVITY.theme);
setContentView(R.layout.activity_record_workout);
setTitle(R.string.recordWorkout);
@ -148,13 +146,7 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
private void setupMap(){
this.mapView= new MapView(this);
TileSources.Purpose purpose;
if(ACTIVITY.equals(Workout.WORKOUT_TYPE_CYCLING)){
purpose= TileSources.Purpose.CYCLING;
}else{
purpose= TileSources.Purpose.OUTDOOR;
}
downloadLayer= MapManager.setupMap(mapView, purpose);
downloadLayer = MapManager.setupMap(mapView);
}
private void updateLine(){

View File

@ -40,9 +40,9 @@ import java.io.File;
import java.io.IOException;
import de.tadris.fitness.R;
import de.tadris.fitness.announcement.VoiceAnnouncements;
import de.tadris.fitness.export.BackupController;
import de.tadris.fitness.export.RestoreController;
import de.tadris.fitness.recording.announcement.VoiceAnnouncements;
import de.tadris.fitness.util.FileUtils;
import de.tadris.fitness.util.unit.UnitUtils;
import de.tadris.fitness.view.ProgressDialogController;

View File

@ -21,13 +21,10 @@ package de.tadris.fitness.activity;
import android.app.AlertDialog;
import android.content.Intent;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Spinner;
@ -66,11 +63,15 @@ public class ShowWorkoutActivity extends WorkoutActivity implements DialogUtils.
initBeforeContent();
setContentView(R.layout.activity_show_workout);
root= findViewById(R.id.showWorkoutRoot);
initRoot();
initAfterContent();
commentView = addText(getString(R.string.comment) + ": " + workout.comment);
String commentStr = getString(R.string.comment) + ": " + workout.comment;
if (workout.edited) {
commentStr = getString(R.string.workoutEdited) + "\n" + commentStr;
}
commentView = addText(commentStr, true);
commentView.setOnClickListener(v -> openEditCommentDialog());
addTitle(getString(R.string.workoutTime));
@ -150,51 +151,6 @@ public class ShowWorkoutActivity extends WorkoutActivity implements DialogUtils.
}
private 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);
textView.setPadding(0, 20, 0, 0);
root.addView(textView);
}
private TextView addText(String text) {
TextView textView= new TextView(this);
textView.setText(text);
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
textView.setTextColor(getThemePrimaryColor());
textView.setPadding(0, 20, 0, 0);
root.addView(textView);
return textView;
}
private void addKeyValue(String key1, String value1) {
addKeyValue(key1, value1, "", "");
}
private 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);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
@ -308,4 +264,8 @@ public class ShowWorkoutActivity extends WorkoutActivity implements DialogUtils.
return super.onOptionsItemSelected(item);
}
@Override
void initRoot() {
root = findViewById(R.id.showWorkoutRoot);
}
}

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
*
@ -32,7 +32,7 @@ public class ShowWorkoutMapActivity extends WorkoutActivity {
initBeforeContent();
setContentView(R.layout.activity_show_workout_map);
root= findViewById(R.id.showWorkoutMapParent);
initRoot();
initAfterContent();
@ -43,4 +43,8 @@ public class ShowWorkoutMapActivity extends WorkoutActivity {
}
@Override
void initRoot() {
root = findViewById(R.id.showWorkoutMapParent);
}
}

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
*
@ -36,7 +36,7 @@ public class ShowWorkoutMapDiagramActivity extends WorkoutActivity {
initBeforeContent();
setContentView(R.layout.activity_show_workout_map_diagram);
root= findViewById(R.id.showWorkoutMapParent);
initRoot();
initAfterContent();
@ -54,4 +54,8 @@ public class ShowWorkoutMapDiagramActivity extends WorkoutActivity {
}
@Override
void initRoot() {
root = findViewById(R.id.showWorkoutMapParent);
}
}

View File

@ -55,18 +55,14 @@ import de.tadris.fitness.data.WorkoutManager;
import de.tadris.fitness.data.WorkoutSample;
import de.tadris.fitness.map.MapManager;
import de.tadris.fitness.map.WorkoutLayer;
import de.tadris.fitness.map.tilesource.TileSources;
import de.tadris.fitness.util.ThemeManager;
import de.tadris.fitness.util.WorkoutTypeCalculator;
import de.tadris.fitness.util.unit.UnitUtils;
public abstract class WorkoutActivity extends FitoTrackActivity {
public abstract class WorkoutActivity extends InformationActivity {
public static Workout selectedWorkout;
List<WorkoutSample> samples;
Workout workout;
ViewGroup root;
private Resources.Theme theme;
MapView map;
private TileDownloadLayer downloadLayer;
@ -79,14 +75,14 @@ public abstract class WorkoutActivity extends FitoTrackActivity {
void initBeforeContent() {
workout= selectedWorkout;
samples= Arrays.asList(Instance.getInstance(this).db.workoutDao().getAllSamplesOfWorkout(workout.id));
setTheme(ThemeManager.getThemeByWorkout(workout));
setTheme(workout.getWorkoutType().theme);
}
void initAfterContent() {
if (getActionBar() != null) {
getActionBar().setDisplayHomeAsUpEnabled(true);
}
setTitle(WorkoutTypeCalculator.getType(workout));
setTitle(workout.getWorkoutType().title);
theme= getTheme();
}
@ -259,7 +255,7 @@ public abstract class WorkoutActivity extends FitoTrackActivity {
void addMap(){
map= new MapView(this);
downloadLayer= MapManager.setupMap(map, TileSources.Purpose.DEFAULT);
downloadLayer = MapManager.setupMap(map);
WorkoutLayer workoutLayer= new WorkoutLayer(samples, getThemePrimaryColor());
map.addLayer(workoutLayer);

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
*
@ -22,7 +22,7 @@ package de.tadris.fitness.data;
import androidx.room.Database;
import androidx.room.RoomDatabase;
@Database(version = 2, entities = {Workout.class, WorkoutSample.class})
@Database(version = 3, entities = {Workout.class, WorkoutSample.class})
public abstract class AppDatabase extends RoomDatabase {
public abstract WorkoutDao workoutDao();
}

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,6 +19,7 @@
package de.tadris.fitness.data;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
@ -31,10 +32,6 @@ import java.util.Date;
@JsonIgnoreProperties(ignoreUnknown = true)
public class Workout{
public static final String WORKOUT_TYPE_RUNNING= "running";
public static final String WORKOUT_TYPE_HIKING= "hiking";
public static final String WORKOUT_TYPE_CYCLING= "cycling";
@PrimaryKey
public long id;
@ -63,12 +60,12 @@ public class Workout{
public double topSpeed;
/**
* Average pace of workout in km/min
* Average pace of workout in min/km
*/
public double avgPace;
public String workoutType;
@ColumnInfo(name = "workoutType")
public String workoutTypeId;
public float ascent;
@ -76,6 +73,8 @@ public class Workout{
public int calorie;
public boolean edited;
public String toString(){
if(comment.length() > 2){
return comment;
@ -88,5 +87,13 @@ public class Workout{
return SimpleDateFormat.getDateTimeInstance().format(new Date(start));
}
public WorkoutType getWorkoutType() {
return WorkoutType.getTypeById(workoutTypeId);
}
public void setWorkoutType(WorkoutType workoutType) {
this.workoutTypeId = workoutType.id;
}
}

View File

@ -0,0 +1,123 @@
/*
* 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.data;
import android.content.Context;
import java.util.Calendar;
import de.tadris.fitness.Instance;
import de.tadris.fitness.util.CalorieCalculator;
public class WorkoutBuilder {
private WorkoutType workoutType;
private Calendar start;
private long duration;
private int length;
private String comment;
public WorkoutBuilder() {
workoutType = WorkoutType.RUNNING;
start = Calendar.getInstance();
duration = 1000L * 60 * 10;
length = 500;
comment = "";
}
public Workout create(Context context) {
Workout workout = new Workout();
// Calculate values
workout.start = start.getTimeInMillis();
workout.end = workout.start + workout.duration;
workout.id = workout.start;
workout.setWorkoutType(workoutType);
workout.duration = duration;
workout.pauseDuration = 0;
workout.length = length;
workout.avgSpeed = (double) length / (double) (duration / 1000);
workout.topSpeed = 0;
workout.avgPace = ((double) workout.duration / 1000 / 60) / ((double) workout.length / 1000);
workout.ascent = 0;
workout.descent = 0;
workout.calorie = CalorieCalculator.calculateCalories(workout, Instance.getInstance(context).userPreferences.getUserWeight());
workout.comment = comment;
workout.edited = true;
return workout;
}
public Workout insertWorkout(Context context) {
Workout workout = create(context);
Instance.getInstance(context).db.workoutDao().insertWorkout(workout);
return workout;
}
public WorkoutType getWorkoutType() {
return workoutType;
}
public void setWorkoutType(WorkoutType workoutType) {
this.workoutType = workoutType;
}
public Calendar getStart() {
return start;
}
public void setStart(Calendar start) {
this.start = start;
}
public long getDuration() {
return duration;
}
public void setDuration(long duration) {
this.duration = duration;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
}

View File

@ -0,0 +1,58 @@
/*
* 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.data;
import androidx.annotation.StringRes;
import androidx.annotation.StyleRes;
import de.tadris.fitness.R;
public enum WorkoutType {
RUNNING("running", R.string.workoutTypeRunning, 7, true, R.style.Running),
HIKING("hiking", R.string.workoutTypeHiking, 7, true, R.style.Hiking),
CYCLING("cycling", R.string.workoutTypeCycling, 12, true, R.style.Bicycling),
OTHER("other", R.string.workoutTypeOther, 7, true, R.style.AppTheme);
public String id;
public @StringRes
int title;
public int minDistance; // Minimum distance between samples
public boolean hasGPS;
public @StyleRes
int theme;
WorkoutType(String id, int title, int minDistance, boolean hasGPS, int theme) {
this.id = id;
this.title = title;
this.minDistance = minDistance;
this.hasGPS = hasGPS;
this.theme = theme;
}
public static WorkoutType getTypeById(String id) {
for (WorkoutType type : values()) {
if (type.id.equals(id)) {
return type;
}
}
return OTHER;
}
}

View File

@ -0,0 +1,54 @@
/*
* 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.dialog;
import android.app.DatePickerDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.os.Bundle;
import android.widget.DatePicker;
import java.util.Calendar;
public class DatePickerFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener {
public DatePickerCallback callback;
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the current date as the default date in the picker
final Calendar c = Calendar.getInstance();
c.setTimeInMillis(System.currentTimeMillis());
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
int day = c.get(Calendar.DAY_OF_MONTH);
// Create a new instance of DatePickerDialog and return it
return new DatePickerDialog(getActivity(), this, year, month, day);
}
public void onDateSet(DatePicker view, int year, int month, int day) {
callback.onDatePick(year, month, day);
}
public interface DatePickerCallback {
void onDatePick(int year, int month, int day);
}
}

View File

@ -0,0 +1,86 @@
/*
* 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.dialog;
import android.app.Activity;
import android.app.AlertDialog;
import android.view.View;
import android.widget.NumberPicker;
import de.tadris.fitness.R;
public class DurationPickerDialogFragment {
public Activity context;
public DurationPickListener listener;
public long initialDuration;
public DurationPickerDialogFragment(Activity context, DurationPickListener listener, long initialDuration) {
this.context = context;
this.listener = listener;
this.initialDuration = initialDuration;
}
public void show() {
final AlertDialog.Builder d = new AlertDialog.Builder(context);
d.setTitle(R.string.setDuration);
View v = context.getLayoutInflater().inflate(R.layout.dialog_duration_picker, null);
NumberPicker hours = v.findViewById(R.id.durationPickerHours);
hours.setFormatter(value -> value + " " + context.getString(R.string.timeHourShort));
hours.setMinValue(0);
hours.setMaxValue(60);
hours.setValue(getInitialHours());
NumberPicker minutes = v.findViewById(R.id.durationPickerMinutes);
minutes.setFormatter(value -> value + " " + context.getString(R.string.timeMinuteShort));
minutes.setMinValue(0);
minutes.setMaxValue(60);
minutes.setValue(getInitialMinutes());
d.setView(v);
d.setNegativeButton(R.string.cancel, null);
d.setPositiveButton(R.string.okay, (dialog, which) -> {
listener.onDurationPick(getMillisFromPick(hours.getValue(), minutes.getValue()));
});
d.create().show();
}
private int getInitialHours() {
return (int) (initialDuration / 1000 / 60 / 60);
}
private int getInitialMinutes() {
return (int) (initialDuration / 1000 / 60 % 60);
}
private static long getMillisFromPick(int hours, int minutes) {
long minuteInMillis = 1000L * 60;
long hourInMillis = minuteInMillis * 60;
return hours * hourInMillis + minutes * minuteInMillis;
}
public interface DurationPickListener {
void onDurationPick(long duration);
}
}

View File

@ -0,0 +1,56 @@
/*
* 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.dialog;
import android.app.Activity;
import android.app.AlertDialog;
import android.widget.ArrayAdapter;
import de.tadris.fitness.R;
import de.tadris.fitness.data.WorkoutType;
public class SelectWorkoutTypeDialog {
private Activity context;
private WorkoutTypeSelectListener listener;
private WorkoutType[] options;
public SelectWorkoutTypeDialog(Activity context, WorkoutTypeSelectListener listener) {
this.context = context;
this.listener = listener;
this.options = WorkoutType.values();
}
public void show() {
AlertDialog.Builder builderSingle = new AlertDialog.Builder(context);
final ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(context, R.layout.select_dialog_singlechoice_material);
for (WorkoutType type : options) {
arrayAdapter.add(context.getString(type.title));
}
builderSingle.setAdapter(arrayAdapter, (dialog, which) -> listener.onSelectWorkoutType(options[which]));
builderSingle.show();
}
public interface WorkoutTypeSelectListener {
void onSelectWorkoutType(WorkoutType workoutType);
}
}

View File

@ -0,0 +1,53 @@
/*
* 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.dialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.TimePickerDialog;
import android.os.Bundle;
import android.text.format.DateFormat;
import android.widget.TimePicker;
import java.util.Calendar;
public class TimePickerFragment extends DialogFragment implements TimePickerDialog.OnTimeSetListener {
public TimePickerCallback callback;
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the current time as the default values for the picker
final Calendar c = Calendar.getInstance();
int hour = c.get(Calendar.HOUR_OF_DAY);
int minute = c.get(Calendar.MINUTE);
// Create a new instance of TimePickerDialog and return it
return new TimePickerDialog(getActivity(), this, hour, minute, DateFormat.is24HourFormat(getActivity()));
}
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
callback.onTimePick(hourOfDay, minute);
}
public interface TimePickerCallback {
void onTimePick(int hour, int minute);
}
}

View File

@ -30,11 +30,10 @@ import de.tadris.fitness.map.tilesource.FitoTrackTileSource;
import de.tadris.fitness.map.tilesource.HumanitarianTileSource;
import de.tadris.fitness.map.tilesource.MapnikTileSource;
import de.tadris.fitness.map.tilesource.ThunderforestTileSource;
import de.tadris.fitness.map.tilesource.TileSources;
public class MapManager {
public static TileDownloadLayer setupMap(MapView mapView, TileSources.Purpose purpose){
public static TileDownloadLayer setupMap(MapView mapView) {
FitoTrackTileSource tileSource;
String chosenTileLayer= Instance.getInstance(mapView.getContext()).userPreferences.getMapStyle();

View File

@ -1,33 +0,0 @@
/*
* 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.map.tilesource;
public class TileSources {
public static FitoTrackTileSource[] tileSources= new FitoTrackTileSource[]{
MapnikTileSource.INSTANCE, HumanitarianTileSource.INSTANCE, ThunderforestTileSource.OUTDOORS, ThunderforestTileSource.CYCLE_MAP
};
public enum Purpose{
DEFAULT, OUTDOOR, CYCLING
}
}

View File

@ -32,21 +32,11 @@ import java.util.List;
import de.tadris.fitness.Instance;
import de.tadris.fitness.data.Workout;
import de.tadris.fitness.data.WorkoutSample;
import de.tadris.fitness.data.WorkoutType;
import de.tadris.fitness.util.CalorieCalculator;
public class WorkoutRecorder implements LocationListener.LocationChangeListener {
private static int getMinDistance(String workoutType){
switch (workoutType){
case Workout.WORKOUT_TYPE_HIKING:
case Workout.WORKOUT_TYPE_RUNNING:
return 8;
case Workout.WORKOUT_TYPE_CYCLING:
return 15;
default: return 10;
}
}
private static final int PAUSE_TIME= 10000;
/**
@ -72,17 +62,18 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
private final WorkoutRecorderListener workoutRecorderListener;
private GpsState gpsState= GpsState.SIGNAL_LOST;
public WorkoutRecorder(Context context, String workoutType, WorkoutRecorderListener workoutRecorderListener) {
public WorkoutRecorder(Context context, WorkoutType workoutType, WorkoutRecorderListener workoutRecorderListener) {
this.context= context;
this.state= RecordingState.IDLE;
this.workoutRecorderListener = workoutRecorderListener;
this.workout= new Workout();
workout.edited = false;
// Default values
this.workout.comment= "";
this.workout.workoutType= workoutType;
this.workout.setWorkoutType(workoutType);
}
public void start(){
@ -214,7 +205,7 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
WorkoutSample lastSample= samples.get(samples.size() - 1);
distance= LocationListener.locationToLatLong(location).sphericalDistance(new LatLong(lastSample.lat, lastSample.lon));
long timediff= lastSample.absoluteTime - location.getTime();
if(distance < getMinDistance(workout.workoutType) && timediff < 500){
if (distance < workout.getWorkoutType().minDistance && timediff < 500) {
return;
}
}

View File

@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.tadris.fitness.announcement;
package de.tadris.fitness.recording.announcement;
import android.content.Context;
import android.preference.PreferenceManager;

View File

@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.tadris.fitness.announcement;
package de.tadris.fitness.recording.announcement;
import android.content.Context;

View File

@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.tadris.fitness.announcement;
package de.tadris.fitness.recording.announcement;
import android.content.Context;

View File

@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.tadris.fitness.announcement;
package de.tadris.fitness.recording.announcement;
import android.content.Context;

View File

@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.tadris.fitness.announcement;
package de.tadris.fitness.recording.announcement;
import android.content.Context;

View File

@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.tadris.fitness.announcement;
package de.tadris.fitness.recording.announcement;
import android.content.Context;

View File

@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.tadris.fitness.announcement;
package de.tadris.fitness.recording.announcement;
import android.content.Context;
import android.preference.PreferenceManager;

View File

@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.tadris.fitness.announcement;
package de.tadris.fitness.recording.announcement;
import android.content.Context;
import android.media.AudioManager;

View File

@ -20,6 +20,7 @@
package de.tadris.fitness.util;
import de.tadris.fitness.data.Workout;
import de.tadris.fitness.data.WorkoutType;
public class CalorieCalculator {
@ -47,12 +48,11 @@ public class CalorieCalculator {
*/
private static double getMET(Workout workout) {
double speedInKmh= workout.avgSpeed * 3.6;
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
WorkoutType type = workout.getWorkoutType();
if (type == WorkoutType.RUNNING || type == WorkoutType.HIKING) {
return Math.max(1.5, speedInKmh*1.117 - 2.1906);
}
if(workout.workoutType.equals(Workout.WORKOUT_TYPE_CYCLING)){
// This is a linear graph based on the website linked above
if (type == WorkoutType.CYCLING) {
return Math.max(3, (speedInKmh-10) / 1.5);
}
return -1;

View File

@ -1,41 +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.util;
import de.tadris.fitness.R;
import de.tadris.fitness.data.Workout;
public class ThemeManager {
public static int getThemeByWorkoutType(String type){
switch (type){
case Workout.WORKOUT_TYPE_RUNNING: return R.style.Running;
case Workout.WORKOUT_TYPE_CYCLING: return R.style.Bicycling;
case Workout.WORKOUT_TYPE_HIKING: return R.style.Hiking;
default: return R.style.AppTheme;
}
}
public static int getThemeByWorkout(Workout workout){
return getThemeByWorkoutType(workout.workoutType);
}
}

View File

@ -1,46 +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.util;
import de.tadris.fitness.R;
import de.tadris.fitness.data.Workout;
public class WorkoutTypeCalculator {
public static int getType(Workout workout){
if(workout.workoutType.equals(Workout.WORKOUT_TYPE_RUNNING)){
if(workout.avgSpeed < 1.9){
return R.string.workoutTypeWalking;
}else if(workout.avgSpeed < 2.7){
return R.string.workoutTypeJogging;
}else{
return R.string.workoutTypeRunning;
}
}
if(workout.workoutType.equals(Workout.WORKOUT_TYPE_CYCLING)){
return R.string.workoutTypeCycling;
}
if(workout.workoutType.equals(Workout.WORKOUT_TYPE_HIKING)){
return R.string.workoutTypeHiking;
}
return R.string.workoutTypeUnknown;
}
}

View File

@ -61,7 +61,7 @@ public class GpxExporter {
track.cmt= workout.comment;
track.desc= workout.comment;
track.src= "FitoTrack";
track.type= workout.workoutType;
track.type = workout.getWorkoutType().id;
track.trkseg= new ArrayList<>();
TrackSegment segment= new TrackSegment();

View File

@ -32,7 +32,6 @@ import java.util.Date;
import de.tadris.fitness.R;
import de.tadris.fitness.data.Workout;
import de.tadris.fitness.util.WorkoutTypeCalculator;
import de.tadris.fitness.util.unit.UnitUtils;
public class WorkoutAdapter extends RecyclerView.Adapter<WorkoutAdapter.WorkoutViewHolder>{
@ -78,7 +77,7 @@ public class WorkoutAdapter extends RecyclerView.Adapter<WorkoutAdapter.WorkoutV
public void onBindViewHolder(WorkoutViewHolder holder, final int position) {
Workout workout= workouts[position];
holder.dateText.setText(SimpleDateFormat.getDateTimeInstance().format(new Date(workout.start)));
holder.typeText.setText(WorkoutTypeCalculator.getType(workout));
holder.typeText.setText(workout.getWorkoutType().title);
if(workout.comment != null){
if(workout.comment.length() > 33){
holder.commentText.setText(workout.comment.substring(0, 30) + "...");

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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/>.
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.EnterWorkoutActivity">
<LinearLayout
android:id="@+id/enterWorkoutRoot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="15dp" />
</ScrollView>

View File

@ -72,6 +72,16 @@
app:fab_label="@string/workoutTypeCycling"
app:fab_size="normal" />
<com.github.clans.fab.FloatingActionButton
android:id="@+id/workoutListEnter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_add_white"
app:fab_colorNormal="@color/colorPrimary"
app:fab_colorPressed="@color/colorPrimaryDark"
app:fab_label="@string/enterWorkout"
app:fab_size="normal" />
</com.github.clans.fab.FloatingActionMenu>

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:orientation="horizontal">
<NumberPicker
android:id="@+id/durationPickerHours"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="30dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=":" />
<NumberPicker
android:id="@+id/durationPickerMinutes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="30dp" />
</LinearLayout>

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginLeft="-15dp"
android:layout_marginRight="-15dp"
android:gravity="center_vertical"
android:background="?android:selectableItemBackground">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:paddingStart="15dp"
android:paddingEnd="15dp">
<TextView
android:id="@+id/lineKey"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:textSize="20sp"
android:textColor="@color/textLighterBlack" />
<LinearLayout
android:id="@+id/lineViewRoot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toStartOf="@+id/lineValue"
android:layout_toEndOf="@+id/lineKey"
android:layout_centerVertical="true"
android:gravity="right|center_vertical"
android:orientation="horizontal">
</LinearLayout>
<TextView
android:id="@+id/lineValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:textSize="20sp"
android:textColor="@color/textLighterBlack"
android:layout_centerVertical="true"
android:textAlignment="textEnd" />
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:dividerHorizontal" />
</LinearLayout>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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/>.
-->
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableStart="?android:attr/listChoiceIndicatorSingle"
android:drawablePadding="20dp"
android:ellipsize="marquee"
android:gravity="center_vertical"
android:minHeight="48dip"
android:paddingStart="20dp"
android:paddingEnd="24dp"
android:textAppearance="@android:style/TextAppearance.Material.Medium"
android:textColor="?android:attr/textColorAlertDialogListItem" />

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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/>.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/actionEnterWorkoutAdd"
android:icon="@drawable/ic_add_white"
android:showAsAction="always"
android:title="@string/save" />
</menu>

View File

@ -49,10 +49,16 @@
<string name="timeMinuteSingular">Minute</string>
<string name="timeMinutePlural">Minutes</string>
<string name="timeMinuteShort">min</string>
<string name="timeHourSingular">Hour</string>
<string name="timeHourPlural">Hours</string>
<string name="timeHourShort">h</string>
<string name="and">and</string>
<string name="errorEnterValidNumber">Enter a valid number</string>
<string name="errorWorkoutAddFuture">The workout cannot be in the future</string>
<string name="errorEnterValidDuration">Enter a valid duration</string>
<string name="announcementGPSStatus">GPS Status</string>
<string name="voiceAnnouncementsTitle">Voice Announcements</string>
@ -83,6 +89,7 @@
<string name="workoutBurnedEnergy">Burned Energy</string>
<string name="workoutTotalEnergy">Total Energy</string>
<string name="workoutEnergyConsumption">Energy Consumption</string>
<string name="workoutEdited">This workout has been edited.</string>
<string name="uploading">Uploading</string>
<string name="enterVerificationCode">Enter Verification Code</string>
<string name="authenticationFailed">Authentication failed.</string>
@ -102,7 +109,15 @@
<string name="workoutTypeJogging">Jogging</string>
<string name="workoutTypeCycling">Cycling</string>
<string name="workoutTypeHiking">Hiking</string>
<string name="workoutTypeOther">Other</string>
<string name="workoutTypeUnknown">Unknown</string>
<string name="type">Type</string>
<string name="custom">Custom</string>
<string name="enterWorkout">Enter Workout</string>
<string name="setDuration">Set Duration</string>
<string name="recordWorkout">Record Workout</string>
@ -155,4 +170,5 @@
<string name="share">Share</string>
<string name="savedToDownloads">Saved to Downloads</string>
<string name="savingFailed">Saving failed</string>
<string name="info">Info</string>
</resources>

View File

@ -23,6 +23,7 @@ import org.junit.Assert;
import org.junit.Test;
import de.tadris.fitness.data.Workout;
import de.tadris.fitness.data.WorkoutType;
import de.tadris.fitness.util.CalorieCalculator;
public class CalorieCalculatorTest {
@ -31,7 +32,7 @@ public class CalorieCalculatorTest {
public void testCalculation() {
Workout workout = new Workout();
workout.avgSpeed = 2.7d;
workout.workoutType = Workout.WORKOUT_TYPE_RUNNING;
workout.setWorkoutType(WorkoutType.RUNNING);
workout.duration = 1000L * 60 * 10;
int calorie = CalorieCalculator.calculateCalories(workout, 80);
System.out.println("Calories: " + calorie);