Merge branch '2.0'

This commit is contained in:
jannis 2019-10-04 13:16:20 +02:00
commit c090479d26
31 changed files with 872 additions and 299 deletions

View File

@ -35,8 +35,8 @@ android {
applicationId "de.tadris.fitness" applicationId "de.tadris.fitness"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 28 targetSdkVersion 28
versionCode 111 versionCode 200
versionName "1.1.1" versionName "2.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
} }
buildTypes { buildTypes {

View File

@ -22,13 +22,14 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
package="de.tadris.fitness"> package="de.tadris.fitness">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application <application
android:allowBackup="true" android:allowBackup="true"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
@ -36,8 +37,13 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:appCategory="productivity"
tools:ignore="GoogleAppIndexingWarning"> tools:ignore="GoogleAppIndexingWarning">
<activity android:name=".activity.SettingsActivity"></activity> <activity android:name=".activity.ShowWorkoutMapActivity"
android:screenOrientation="portrait"></activity>
<activity android:name=".activity.ShowWorkoutMapDiagramActivity"
android:screenOrientation="portrait"></activity>
<activity android:name=".activity.SettingsActivity" />
<activity <activity
android:name=".activity.ShowWorkoutActivity" android:name=".activity.ShowWorkoutActivity"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
@ -56,15 +62,17 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
android:authorities="de.tadris.fitness.fileprovider" android:authorities="de.tadris.fitness.fileprovider"
android:grantUriPermissions="true" android:exported="false"
android:exported="false"> android:grantUriPermissions="true">
<meta-data <meta-data
android:name="android.support.FILE_PROVIDER_PATHS" android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" /> android:resource="@xml/filepaths" />
</provider> </provider>
<service android:name=".recording.LocationListener" /> <service android:name=".recording.LocationListener" />
<service android:name=".recording.PressureService" /> <service android:name=".recording.PressureService" />
</application> </application>

View File

@ -37,6 +37,7 @@ import com.github.clans.fab.FloatingActionMenu;
import de.tadris.fitness.Instance; import de.tadris.fitness.Instance;
import de.tadris.fitness.R; import de.tadris.fitness.R;
import de.tadris.fitness.data.Workout; import de.tadris.fitness.data.Workout;
import de.tadris.fitness.util.DialogUtils;
import de.tadris.fitness.view.WorkoutAdapter; import de.tadris.fitness.view.WorkoutAdapter;
public class ListWorkoutsActivity extends Activity implements WorkoutAdapter.WorkoutAdapterListener { public class ListWorkoutsActivity extends Activity implements WorkoutAdapter.WorkoutAdapterListener {
@ -73,8 +74,13 @@ public class ListWorkoutsActivity extends Activity implements WorkoutAdapter.Wor
findViewById(R.id.workoutListRecordHiking) .setOnClickListener(v -> startRecording(Workout.WORKOUT_TYPE_HIKING)); 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.workoutListRecordCycling).setOnClickListener(v -> startRecording(Workout.WORKOUT_TYPE_CYCLING));
loadData();
checkFirstStart(); checkFirstStart();
adapter= new WorkoutAdapter(workouts, this);
listView.setAdapter(adapter);
} }
private void checkFirstStart(){ private void checkFirstStart(){
@ -101,9 +107,8 @@ public class ListWorkoutsActivity extends Activity implements WorkoutAdapter.Wor
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
workouts= Instance.getInstance(this).db.workoutDao().getWorkouts(); loadData();
adapter= new WorkoutAdapter(workouts, this); adapter.notifyDataSetChanged();
listView.setAdapter(adapter);
} }
@Override @Override
@ -112,6 +117,19 @@ public class ListWorkoutsActivity extends Activity implements WorkoutAdapter.Wor
startActivity(new Intent(this, ShowWorkoutActivity.class)); startActivity(new Intent(this, ShowWorkoutActivity.class));
} }
@Override
public void onItemLongClick(int pos, Workout workout) {
DialogUtils.showDeleteWorkoutDialog(this, () -> {
Instance.getInstance(ListWorkoutsActivity.this).db.workoutDao().deleteWorkout(workout);
loadData();
adapter.notifyItemRemoved(pos);
});
}
private void loadData(){
workouts= Instance.getInstance(this).db.workoutDao().getWorkouts();
}
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present. // Inflate the menu; this adds items to the action bar if it is present.
@ -131,6 +149,4 @@ public class ListWorkoutsActivity extends Activity implements WorkoutAdapter.Wor
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
} }

View File

@ -20,6 +20,7 @@
package de.tadris.fitness.activity; package de.tadris.fitness.activity;
import android.Manifest; import android.Manifest;
import android.animation.Animator;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@ -32,6 +33,7 @@ import android.os.Handler;
import android.os.PowerManager; import android.os.PowerManager;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
@ -71,6 +73,8 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
List<LatLong> latLongList= new ArrayList<>(); List<LatLong> latLongList= new ArrayList<>();
InfoViewHolder[] infoViews= new InfoViewHolder[4]; InfoViewHolder[] infoViews= new InfoViewHolder[4];
TextView timeView, gpsStatusView; TextView timeView, gpsStatusView;
View waitingForGPSOverlay;
boolean gpsFound= false;
boolean isResumed= false; boolean isResumed= false;
private Handler mHandler= new Handler(); private Handler mHandler= new Handler();
PowerManager.WakeLock wakeLock; PowerManager.WakeLock wakeLock;
@ -89,6 +93,8 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
setupMap(); setupMap();
((ViewGroup)findViewById(R.id.recordMapViewrRoot)).addView(mapView); ((ViewGroup)findViewById(R.id.recordMapViewrRoot)).addView(mapView);
waitingForGPSOverlay= findViewById(R.id.recorderWaitingOverlay);
waitingForGPSOverlay.setVisibility(View.VISIBLE);
checkPermissions(); checkPermissions();
@ -119,6 +125,20 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
wakeLock.acquire(1000*60*120); wakeLock.acquire(1000*60*120);
} }
private void hideWaitOverlay(){
waitingForGPSOverlay.clearAnimation();
waitingForGPSOverlay.animate().alpha(0f).setDuration(1000).setListener(new Animator.AnimatorListener() {
@Override public void onAnimationStart(Animator animator) { }
@Override public void onAnimationCancel(Animator animator) { }
@Override public void onAnimationRepeat(Animator animator) { }
@Override
public void onAnimationEnd(Animator animator) {
waitingForGPSOverlay.setVisibility(View.GONE);
}
}).start();
}
private void setupMap(){ private void setupMap(){
this.mapView= new MapView(this); this.mapView= new MapView(this);
TileSources.Purpose purpose; TileSources.Purpose purpose;
@ -336,7 +356,13 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
@Override @Override
public void onGPSStateChanged(WorkoutRecorder.GpsState oldState, WorkoutRecorder.GpsState state) { public void onGPSStateChanged(WorkoutRecorder.GpsState oldState, WorkoutRecorder.GpsState state) {
mHandler.post(() -> gpsStatusView.setTextColor(state.color)); mHandler.post(() -> {
gpsStatusView.setTextColor(state.color);
if(!gpsFound && (state != WorkoutRecorder.GpsState.SIGNAL_LOST)){
gpsFound= true;
hideWaitOverlay();
}
});
} }
public static class InfoViewHolder{ public static class InfoViewHolder{

View File

@ -22,7 +22,6 @@ package de.tadris.fitness.activity;
import android.Manifest; import android.Manifest;
import android.app.ActionBar; import android.app.ActionBar;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@ -44,7 +43,6 @@ import android.widget.NumberPicker;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.core.app.NavUtils;
import androidx.core.content.FileProvider; import androidx.core.content.FileProvider;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
@ -53,7 +51,6 @@ import java.io.FileNotFoundException;
import de.tadris.fitness.R; import de.tadris.fitness.R;
import de.tadris.fitness.util.export.Exporter; import de.tadris.fitness.util.export.Exporter;
import de.tadris.fitness.util.gpx.GpxExporter;
import de.tadris.fitness.util.unit.UnitUtils; import de.tadris.fitness.util.unit.UnitUtils;
import de.tadris.fitness.view.ProgressDialogController; import de.tadris.fitness.view.ProgressDialogController;
@ -160,6 +157,7 @@ public class SettingsActivity extends PreferenceActivity {
addPreferencesFromResource(R.xml.preferences_main); addPreferencesFromResource(R.xml.preferences_main);
bindPreferenceSummaryToValue(findPreference("unitSystem")); bindPreferenceSummaryToValue(findPreference("unitSystem"));
bindPreferenceSummaryToValue(findPreference("mapStyle"));
findPreference("weight").setOnPreferenceClickListener(preference -> showWeightPicker()); findPreference("weight").setOnPreferenceClickListener(preference -> showWeightPicker());
findPreference("import").setOnPreferenceClickListener(preference -> showImportDialog()); findPreference("import").setOnPreferenceClickListener(preference -> showImportDialog());
@ -259,10 +257,8 @@ public class SettingsActivity extends PreferenceActivity {
Exporter.importData(getBaseContext(), uri, Exporter.importData(getBaseContext(), uri,
(progress, action) -> mHandler.post(() -> dialogController.setProgress(progress, action))); (progress, action) -> mHandler.post(() -> dialogController.setProgress(progress, action)));
mHandler.post(() -> {
// DO on backup finished // DO on backup finished
dialogController.cancel(); mHandler.post(dialogController::cancel);
});
}catch (Exception e){ }catch (Exception e){
e.printStackTrace(); e.printStackTrace();
mHandler.post(() -> { mHandler.post(() -> {

View File

@ -20,88 +20,44 @@
package de.tadris.fitness.activity; package de.tadris.fitness.activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.res.Resources; import android.content.Intent;
import android.graphics.Color;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import androidx.core.content.FileProvider; import androidx.core.content.FileProvider;
import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.Description;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.listener.OnChartValueSelectedListener;
import org.mapsforge.core.graphics.Paint;
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 org.mapsforge.map.layer.overlay.FixedPixelCircle;
import java.io.File; import java.io.File;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.List;
import de.tadris.fitness.Instance; import de.tadris.fitness.Instance;
import de.tadris.fitness.R; import de.tadris.fitness.R;
import de.tadris.fitness.data.Workout; import de.tadris.fitness.util.DialogUtils;
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.gpx.GpxExporter; import de.tadris.fitness.util.gpx.GpxExporter;
import de.tadris.fitness.util.unit.UnitUtils; import de.tadris.fitness.util.unit.UnitUtils;
import de.tadris.fitness.view.ProgressDialogController; import de.tadris.fitness.view.ProgressDialogController;
public class ShowWorkoutActivity extends FitoTrackActivity { public class ShowWorkoutActivity extends WorkoutActivity implements DialogUtils.WorkoutDeleter {
static Workout selectedWorkout;
List<WorkoutSample> samples;
Workout workout;
ViewGroup root;
Resources.Theme theme;
MapView map;
TileDownloadLayer downloadLayer;
FixedPixelCircle highlightingCircle;
Handler mHandler= new Handler();
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
workout= selectedWorkout; initBeforeContent();
samples= Arrays.asList(Instance.getInstance(this).db.workoutDao().getAllSamplesOfWorkout(workout.id));
setTheme(ThemeManager.getThemeByWorkout(workout));
setContentView(R.layout.activity_show_workout); setContentView(R.layout.activity_show_workout);
getActionBar().setDisplayHomeAsUpEnabled(true);
setTitle(WorkoutTypeCalculator.getType(workout));
theme= getTheme();
root= findViewById(R.id.showWorkoutRoot); root= findViewById(R.id.showWorkoutRoot);
initAfterContent();
addText(getString(R.string.comment) + ": " + workout.comment).setOnClickListener(v -> { addText(getString(R.string.comment) + ": " + workout.comment).setOnClickListener(v -> {
TextView textView= (TextView)v; TextView textView= (TextView)v;
openEditCommentDialog(textView); openEditCommentDialog(textView);
@ -120,6 +76,9 @@ public class ShowWorkoutActivity extends FitoTrackActivity {
addMap(); addMap();
map.setClickable(false);
mapRoot.setOnClickListener(v -> startActivity(new Intent(ShowWorkoutActivity.this, ShowWorkoutMapActivity.class)));
addTitle(getString(R.string.workoutSpeed)); addTitle(getString(R.string.workoutSpeed));
addKeyValue(getString(R.string.workoutAvgSpeed), UnitUtils.getSpeed(workout.avgSpeed), addKeyValue(getString(R.string.workoutAvgSpeed), UnitUtils.getSpeed(workout.avgSpeed),
@ -127,6 +86,8 @@ public class ShowWorkoutActivity extends FitoTrackActivity {
addSpeedDiagram(); addSpeedDiagram();
speedDiagram.setOnClickListener(v -> startDiagramActivity(ShowWorkoutMapDiagramActivity.DIAGRAM_TYPE_SPEED));
addTitle(getString(R.string.workoutBurnedEnergy)); addTitle(getString(R.string.workoutBurnedEnergy));
addKeyValue(getString(R.string.workoutTotalEnergy), workout.calorie + " kcal", addKeyValue(getString(R.string.workoutTotalEnergy), workout.calorie + " kcal",
getString(R.string.workoutEnergyConsumption), UnitUtils.getRelativeEnergyConsumption((double)workout.calorie / ((double)workout.length / 1000))); getString(R.string.workoutEnergyConsumption), UnitUtils.getRelativeEnergyConsumption((double)workout.calorie / ((double)workout.length / 1000)));
@ -138,9 +99,17 @@ public class ShowWorkoutActivity extends FitoTrackActivity {
addHeightDiagram(); addHeightDiagram();
heightDiagram.setOnClickListener(v -> startDiagramActivity(ShowWorkoutMapDiagramActivity.DIAGRAM_TYPE_HEIGHT));
} }
void startDiagramActivity(String diagramType){
ShowWorkoutMapDiagramActivity.DIAGRAM_TYPE= diagramType;
startActivity(new Intent(ShowWorkoutActivity.this, ShowWorkoutMapDiagramActivity.class));
}
void openEditCommentDialog(final TextView change){ void openEditCommentDialog(final TextView change){
final EditText editText= new EditText(this); final EditText editText= new EditText(this);
editText.setText(workout.comment); editText.setText(workout.comment);
@ -206,186 +175,6 @@ public class ShowWorkoutActivity extends FitoTrackActivity {
root.addView(v); root.addView(v);
} }
void addDiagram(SampleConverter converter){
LineChart chart= new LineChart(this);
converter.onCreate();
List<Entry> entries = new ArrayList<>();
for (WorkoutSample sample : samples) {
// turn your data into Entry objects
Entry e= new Entry((float)(sample.relativeTime) / 1000f / 60f, converter.getValue(sample));
entries.add(e);
converter.sampleGetsEntry(sample, e);
}
LineDataSet dataSet = new LineDataSet(entries, converter.getName()); // add entries to dataset
dataSet.setColor(getThemePrimaryColor());
dataSet.setValueTextColor(getThemePrimaryColor());
dataSet.setDrawCircles(false);
dataSet.setLineWidth(4);
dataSet.setMode(LineDataSet.Mode.CUBIC_BEZIER);
Description description= new Description();
description.setText(converter.getDescription());
LineData lineData = new LineData(dataSet);
chart.setData(lineData);
chart.setScaleXEnabled(true);
chart.setScaleYEnabled(false);
chart.setDescription(description);
chart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() {
@Override
public void onValueSelected(Entry e, Highlight h) {
onNothingSelected();
Paint p= AndroidGraphicFactory.INSTANCE.createPaint();
p.setColor(Color.BLUE);
highlightingCircle= new FixedPixelCircle(findSample(converter, e).toLatLong(), 10, p, null);
map.addLayer(highlightingCircle);
}
@Override
public void onNothingSelected() {
if(highlightingCircle != null){
map.getLayerManager().getLayers().remove(highlightingCircle);
}
}
});
chart.invalidate();
root.addView(chart, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getWindowManager().getDefaultDisplay().getWidth()*3/4));
}
interface SampleConverter{
void onCreate();
float getValue(WorkoutSample sample);
void sampleGetsEntry(WorkoutSample sample, Entry entry);
String getName();
String getDescription();
boolean compare(WorkoutSample sample, Entry entry);
}
void addHeightDiagram(){
addDiagram(new SampleConverter() {
@Override
public void onCreate() { }
@Override
public float getValue(WorkoutSample sample) {
return (float)UnitUtils.CHOSEN_SYSTEM.getDistanceFromMeters(sample.elevation);
}
@Override
public void sampleGetsEntry(WorkoutSample sample, Entry entry) {
sample.tmpHeightEntry= entry;
}
@Override
public String getName() {
return getString(R.string.height);
}
@Override
public String getDescription() {
return "min - " + UnitUtils.CHOSEN_SYSTEM.getShortDistanceUnit();
}
@Override
public boolean compare(WorkoutSample sample, Entry entry) {
return sample.tmpHeightEntry.equalTo(entry);
}
});
}
void addSpeedDiagram(){
addDiagram(new SampleConverter() {
@Override
public void onCreate() {
WorkoutManager.roundSpeedValues(samples);
}
@Override
public float getValue(WorkoutSample sample) {
return (float)UnitUtils.CHOSEN_SYSTEM.getSpeedFromMeterPerSecond(sample.tmpRoundedSpeed);
}
@Override
public void sampleGetsEntry(WorkoutSample sample, Entry entry) {
sample.tmpSpeedEntry= entry;
}
@Override
public String getName() {
return getString(R.string.workoutSpeed);
}
@Override
public String getDescription() {
return "min - " + UnitUtils.CHOSEN_SYSTEM.getSpeedUnit();
}
@Override
public boolean compare(WorkoutSample sample, Entry entry) {
return sample.tmpSpeedEntry.equalTo(entry);
}
});
}
WorkoutSample findSample(SampleConverter converter, Entry entry){
for(WorkoutSample sample : samples){
if(converter.compare(sample, entry)){
return sample;
}
}
return null;
}
void addMap(){
map= new MapView(this);
downloadLayer= MapManager.setupMap(map, TileSources.Purpose.DEFAULT);
WorkoutLayer workoutLayer= new WorkoutLayer(samples, getThemePrimaryColor());
map.addLayer(workoutLayer);
final BoundingBox bounds= new BoundingBox(workoutLayer.getLatLongs()).extendMeters(50);
mHandler.postDelayed(() -> {
map.getModel().mapViewPosition.setMapPosition(new MapPosition(bounds.getCenterPoint(),
(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);
Paint pGreen= AndroidGraphicFactory.INSTANCE.createPaint();
pGreen.setColor(Color.GREEN);
map.addLayer(new FixedPixelCircle(samples.get(0).toLatLong(), 20, pGreen, null));
Paint pRed= AndroidGraphicFactory.INSTANCE.createPaint();
pRed.setColor(Color.RED);
map.addLayer(new FixedPixelCircle(samples.get(samples.size()-1).toLatLong(), 20, pRed, null));
}
@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 @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
@ -394,17 +183,13 @@ public class ShowWorkoutActivity extends FitoTrackActivity {
return true; return true;
} }
private void deleteWorkout(){ public void deleteWorkout(){
Instance.getInstance(this).db.workoutDao().deleteWorkout(workout); Instance.getInstance(this).db.workoutDao().deleteWorkout(workout);
finish(); finish();
} }
private void showDeleteDialog(){ private void showDeleteDialog(){
new AlertDialog.Builder(this).setTitle(R.string.deleteWorkout) DialogUtils.showDeleteWorkoutDialog(this, this);
.setMessage(R.string.deleteWorkoutMessage)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.delete, (dialog, which) -> deleteWorkout())
.create().show();
} }
private void exportToGpx(){ private void exportToGpx(){
@ -438,9 +223,6 @@ public class ShowWorkoutActivity extends FitoTrackActivity {
case R.id.actionExportGpx: case R.id.actionExportGpx:
exportToGpx(); exportToGpx();
return true; return true;
case android.R.id.home:
finish();
return true;
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }

View File

@ -0,0 +1,46 @@
/*
* 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.activity;
import android.os.Bundle;
import de.tadris.fitness.R;
public class ShowWorkoutMapActivity extends WorkoutActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initBeforeContent();
setContentView(R.layout.activity_show_workout_map);
root= findViewById(R.id.showWorkoutMapParent);
initAfterContent();
fullScreenItems = true;
addMap();
map.setClickable(true);
}
}

View File

@ -0,0 +1,57 @@
/*
* 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.activity;
import android.os.Bundle;
import de.tadris.fitness.R;
public class ShowWorkoutMapDiagramActivity extends WorkoutActivity {
public static final String DIAGRAM_TYPE_HEIGHT= "height";
public static final String DIAGRAM_TYPE_SPEED= "speed";
static String DIAGRAM_TYPE;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initBeforeContent();
setContentView(R.layout.activity_show_workout_map_diagram);
root= findViewById(R.id.showWorkoutMapParent);
initAfterContent();
fullScreenItems = true;
addMap();
map.setClickable(true);
diagramsInteractive= true;
root= findViewById(R.id.showWorkoutDiagramParent);
switch (DIAGRAM_TYPE){
case DIAGRAM_TYPE_HEIGHT: addHeightDiagram(); break;
case DIAGRAM_TYPE_SPEED: addSpeedDiagram(); break;
}
}
}

View File

@ -0,0 +1,316 @@
/*
* 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.activity;
import android.content.res.Resources;
import android.graphics.Color;
import android.os.Handler;
import android.view.MenuItem;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.Description;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.listener.OnChartValueSelectedListener;
import org.mapsforge.core.graphics.Paint;
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 org.mapsforge.map.layer.overlay.FixedPixelCircle;
import java.util.ArrayList;
import java.util.Arrays;
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.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 static Workout selectedWorkout;
protected List<WorkoutSample> samples;
protected Workout workout;
protected ViewGroup root;
protected Resources.Theme theme;
protected MapView map;
protected TileDownloadLayer downloadLayer;
protected FixedPixelCircle highlightingCircle;
protected Handler mHandler= new Handler();
protected LineChart speedDiagram, heightDiagram;
protected void initBeforeContent(){
workout= selectedWorkout;
samples= Arrays.asList(Instance.getInstance(this).db.workoutDao().getAllSamplesOfWorkout(workout.id));
setTheme(ThemeManager.getThemeByWorkout(workout));
}
protected void initAfterContent(){
getActionBar().setDisplayHomeAsUpEnabled(true);
setTitle(WorkoutTypeCalculator.getType(workout));
theme= getTheme();
}
void addDiagram(SampleConverter converter){
root.addView(getDiagram(converter), new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, fullScreenItems ? ViewGroup.LayoutParams.MATCH_PARENT : getWindowManager().getDefaultDisplay().getWidth()*3/4));
}
protected boolean diagramsInteractive= false;
LineChart getDiagram(SampleConverter converter){
LineChart chart= new LineChart(this);
converter.onCreate();
List<Entry> entries = new ArrayList<>();
for (WorkoutSample sample : samples) {
// turn your data into Entry objects
Entry e= new Entry((float)(sample.relativeTime) / 1000f / 60f, converter.getValue(sample));
entries.add(e);
converter.sampleGetsEntry(sample, e);
}
LineDataSet dataSet = new LineDataSet(entries, converter.getName()); // add entries to dataset
dataSet.setColor(getThemePrimaryColor());
dataSet.setValueTextColor(getThemePrimaryColor());
dataSet.setDrawCircles(false);
dataSet.setLineWidth(4);
dataSet.setMode(LineDataSet.Mode.CUBIC_BEZIER);
Description description= new Description();
description.setText(converter.getDescription());
LineData lineData = new LineData(dataSet);
chart.setData(lineData);
chart.setScaleXEnabled(diagramsInteractive);
chart.setScaleYEnabled(false);
chart.setDescription(description);
if(diagramsInteractive){
chart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() {
@Override
public void onValueSelected(Entry e, Highlight h) {
onNothingSelected();
Paint p= AndroidGraphicFactory.INSTANCE.createPaint();
p.setColor(0xff693cff);
highlightingCircle= new FixedPixelCircle(findSample(converter, e).toLatLong(), 20, p, null);
map.addLayer(highlightingCircle);
}
@Override
public void onNothingSelected() {
if(highlightingCircle != null){
map.getLayerManager().getLayers().remove(highlightingCircle);
}
}
});
}
chart.invalidate();
converter.afterAdd(chart);
return chart;
}
interface SampleConverter{
void onCreate();
float getValue(WorkoutSample sample);
void sampleGetsEntry(WorkoutSample sample, Entry entry);
String getName();
String getDescription();
boolean compare(WorkoutSample sample, Entry entry);
void afterAdd(LineChart chart);
}
WorkoutSample findSample(SampleConverter converter, Entry entry){
for(WorkoutSample sample : samples){
if(converter.compare(sample, entry)){
return sample;
}
}
return null;
}
void addHeightDiagram(){
addDiagram(new SampleConverter() {
@Override
public void onCreate() { }
@Override
public float getValue(WorkoutSample sample) {
return (float) UnitUtils.CHOSEN_SYSTEM.getDistanceFromMeters(sample.elevation);
}
@Override
public void sampleGetsEntry(WorkoutSample sample, Entry entry) {
sample.tmpHeightEntry= entry;
}
@Override
public String getName() {
return getString(R.string.height);
}
@Override
public String getDescription() {
return "min - " + UnitUtils.CHOSEN_SYSTEM.getShortDistanceUnit();
}
@Override
public boolean compare(WorkoutSample sample, Entry entry) {
return sample.tmpHeightEntry.equalTo(entry);
}
@Override
public void afterAdd(LineChart chart) {
heightDiagram= chart;
}
});
}
void addSpeedDiagram(){
addDiagram(new SampleConverter() {
@Override
public void onCreate() {
WorkoutManager.roundSpeedValues(samples);
}
@Override
public float getValue(WorkoutSample sample) {
return (float)UnitUtils.CHOSEN_SYSTEM.getSpeedFromMeterPerSecond(sample.tmpRoundedSpeed);
}
@Override
public void sampleGetsEntry(WorkoutSample sample, Entry entry) {
sample.tmpSpeedEntry= entry;
}
@Override
public String getName() {
return getString(R.string.workoutSpeed);
}
@Override
public String getDescription() {
return "min - " + UnitUtils.CHOSEN_SYSTEM.getSpeedUnit();
}
@Override
public boolean compare(WorkoutSample sample, Entry entry) {
return sample.tmpSpeedEntry.equalTo(entry);
}
@Override
public void afterAdd(LineChart chart) {
speedDiagram= chart;
}
});
}
protected boolean fullScreenItems = false;
protected LinearLayout mapRoot;
void addMap(){
map= new MapView(this);
downloadLayer= MapManager.setupMap(map, TileSources.Purpose.DEFAULT);
WorkoutLayer workoutLayer= new WorkoutLayer(samples, getThemePrimaryColor());
map.addLayer(workoutLayer);
final BoundingBox bounds= new BoundingBox(workoutLayer.getLatLongs()).extendMeters(50);
mHandler.postDelayed(() -> {
map.getModel().mapViewPosition.setMapPosition(new MapPosition(bounds.getCenterPoint(),
(LatLongUtils.zoomForBounds(map.getDimension(), bounds, map.getModel().displayModel.getTileSize()))));
map.animate().alpha(1f).setDuration(1000).start();
}, 1000);
map.getModel().mapViewPosition.setMapLimit(bounds);
mapRoot= new LinearLayout(this);
mapRoot.setOrientation(LinearLayout.VERTICAL);
mapRoot.addView(map);
root.addView(mapRoot, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, fullScreenItems ? ViewGroup.LayoutParams.MATCH_PARENT : getMapHeight()));
map.setAlpha(0);
Paint pGreen= AndroidGraphicFactory.INSTANCE.createPaint();
pGreen.setColor(Color.GREEN);
map.addLayer(new FixedPixelCircle(samples.get(0).toLatLong(), 20, pGreen, null));
Paint pRed= AndroidGraphicFactory.INSTANCE.createPaint();
pRed.setColor(Color.RED);
map.addLayer(new FixedPixelCircle(samples.get(samples.size()-1).toLatLong(), 20, pRed, null));
map.setClickable(false);
}
int getMapHeight(){
return getWindowManager().getDefaultDisplay().getWidth()*3/4;
}
@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 onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}

View File

@ -35,4 +35,8 @@ public class UserPreferences {
return preferences.getInt("weight", 80); return preferences.getInt("weight", 80);
} }
public String getMapStyle(){
return preferences.getString("mapStyle", "osm.mapnik");
}
} }

View File

@ -26,21 +26,24 @@ import org.mapsforge.map.android.view.MapView;
import org.mapsforge.map.layer.cache.TileCache; import org.mapsforge.map.layer.cache.TileCache;
import org.mapsforge.map.layer.download.TileDownloadLayer; import org.mapsforge.map.layer.download.TileDownloadLayer;
import de.tadris.fitness.Instance;
import de.tadris.fitness.map.tilesource.FitoTrackTileSource; 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.MapnikTileSource;
import de.tadris.fitness.map.tilesource.ThunderforestTileSource;
import de.tadris.fitness.map.tilesource.TileSources; import de.tadris.fitness.map.tilesource.TileSources;
public class MapManager { public class MapManager {
public static TileDownloadLayer setupMap(MapView mapView, TileSources.Purpose purpose){ public static TileDownloadLayer setupMap(MapView mapView, TileSources.Purpose purpose){
FitoTrackTileSource tileSource; FitoTrackTileSource tileSource;
switch (purpose){
/*case OUTDOOR: tileSource= ThunderforestTileSource.OUTDOORS; break;
case CYCLING: tileSource= ThunderforestTileSource.CYLE_MAP; break;*/
case DEFAULT: String chosenTileLayer= Instance.getInstance(mapView.getContext()).userPreferences.getMapStyle();
default: switch (chosenTileLayer){
tileSource= MapnikTileSource.INSTANCE; break; case "osm.humanitarian": tileSource= HumanitarianTileSource.INSTANCE; break;
case "thunderforest.outdoors": tileSource= ThunderforestTileSource.OUTDOORS; break;
case "thunderforest.cycle": tileSource= ThunderforestTileSource.CYLE_MAP; break;
default: tileSource= MapnikTileSource.INSTANCE; break; // Inclusive "osm.mapnik"
} }
tileSource.setUserAgent("mapsforge-android"); tileSource.setUserAgent("mapsforge-android");

View File

@ -0,0 +1,22 @@
package de.tadris.fitness.util;
import android.app.AlertDialog;
import android.content.Context;
import de.tadris.fitness.R;
public class DialogUtils {
public static void showDeleteWorkoutDialog(Context context, WorkoutDeleter deleter){
new AlertDialog.Builder(context).setTitle(R.string.deleteWorkout)
.setMessage(R.string.deleteWorkoutMessage)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.delete, (dialog, which) -> deleter.deleteWorkout())
.create().show();
}
public interface WorkoutDeleter{
void deleteWorkout();
}
}

View File

@ -29,7 +29,6 @@ import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -59,6 +58,7 @@ public class Exporter {
listener.onStatusChanged(10, context.getString(R.string.preferences)); listener.onStatusChanged(10, context.getString(R.string.preferences));
FitoTrackSettings settings= new FitoTrackSettings(); FitoTrackSettings settings= new FitoTrackSettings();
settings.weight= preferences.getUserWeight(); settings.weight= preferences.getUserWeight();
settings.mapStyle= preferences.getMapStyle();
settings.preferredUnitSystem= String.valueOf(UnitUtils.CHOSEN_SYSTEM.getId()); settings.preferredUnitSystem= String.valueOf(UnitUtils.CHOSEN_SYSTEM.getId());
container.settings= settings; container.settings= settings;
@ -76,14 +76,14 @@ public class Exporter {
} }
@SuppressLint("ApplySharedPref") @SuppressLint("ApplySharedPref")
public static void importData(Context context, Uri input, ExportStatusListener listener) throws IOException{ public static void importData(Context context, Uri input, ExportStatusListener listener) throws IOException, UnsupportedVersionException {
listener.onStatusChanged(0, context.getString(R.string.loadingFile)); listener.onStatusChanged(0, context.getString(R.string.loadingFile));
XmlMapper xmlMapper = new XmlMapper(); XmlMapper xmlMapper = new XmlMapper();
xmlMapper.configure(JsonParser.Feature.IGNORE_UNDEFINED, true); xmlMapper.configure(JsonParser.Feature.IGNORE_UNDEFINED, true);
FitoTrackDataContainer container = xmlMapper.readValue(context.getContentResolver().openInputStream(input), FitoTrackDataContainer.class); FitoTrackDataContainer container = xmlMapper.readValue(context.getContentResolver().openInputStream(input), FitoTrackDataContainer.class);
if(container.version != 1){ if(container.version != 1){
throw new UnsupportedEncodingException("Version Code" + container.version + " is unsupported!"); throw new UnsupportedVersionException("Version Code" + container.version + " is unsupported!");
} }
listener.onStatusChanged(40, context.getString(R.string.preferences)); listener.onStatusChanged(40, context.getString(R.string.preferences));
@ -91,10 +91,12 @@ public class Exporter {
.edit().clear() .edit().clear()
.putInt("weight", container.settings.weight) .putInt("weight", container.settings.weight)
.putString("unitSystem", container.settings.preferredUnitSystem) .putString("unitSystem", container.settings.preferredUnitSystem)
.putBoolean("firstStart", false) .putBoolean("firstStart", false).putString("mapStyle", container.settings.mapStyle)
.commit(); .commit();
AppDatabase database= Instance.getInstance(context).db; AppDatabase database= Instance.getInstance(context).db;
database.runInTransaction(() -> {
database.clearAllTables(); database.clearAllTables();
listener.onStatusChanged(60, context.getString(R.string.workouts)); listener.onStatusChanged(60, context.getString(R.string.workouts));
@ -110,6 +112,8 @@ public class Exporter {
database.workoutDao().insertSample(sample); database.workoutDao().insertSample(sample);
} }
} }
});
listener.onStatusChanged(100, context.getString(R.string.finished)); listener.onStatusChanged(100, context.getString(R.string.finished));
} }

View File

@ -1,15 +1,36 @@
/*
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
*
* This file is part of FitoTrack
*
* FitoTrack is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FitoTrack is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.tadris.fitness.util.export; package de.tadris.fitness.util.export;
public class FitoTrackSettings { public class FitoTrackSettings {
String preferredUnitSystem; String preferredUnitSystem;
int weight; int weight;
String mapStyle;
public FitoTrackSettings(){} public FitoTrackSettings(){}
public FitoTrackSettings(String preferredUnitSystem, int weight) { public FitoTrackSettings(String preferredUnitSystem, int weight, String mapStyle) {
this.preferredUnitSystem = preferredUnitSystem; this.preferredUnitSystem = preferredUnitSystem;
this.weight = weight; this.weight = weight;
this.mapStyle = mapStyle;
} }
public String getPreferredUnitSystem() { public String getPreferredUnitSystem() {
@ -27,4 +48,12 @@ public class FitoTrackSettings {
public void setWeight(int weight) { public void setWeight(int weight) {
this.weight = weight; this.weight = weight;
} }
public String getMapStyle() {
return mapStyle;
}
public void setMapStyle(String mapStyle) {
this.mapStyle = mapStyle;
}
} }

View File

@ -87,6 +87,10 @@ public class WorkoutAdapter extends RecyclerView.Adapter<WorkoutAdapter.WorkoutV
holder.lengthText.setText(UnitUtils.getDistance(workout.length)); holder.lengthText.setText(UnitUtils.getDistance(workout.length));
holder.timeText.setText(UnitUtils.getHourMinuteTime(workout.duration)); holder.timeText.setText(UnitUtils.getHourMinuteTime(workout.duration));
holder.root.setOnClickListener(v -> listener.onItemClick(workout)); holder.root.setOnClickListener(v -> listener.onItemClick(workout));
holder.root.setOnLongClickListener(v -> {
listener.onItemLongClick(position, workout);
return true;
});
} }
// Return the size of your dataset (invoked by the layout manager) // Return the size of your dataset (invoked by the layout manager)
@ -97,6 +101,7 @@ public class WorkoutAdapter extends RecyclerView.Adapter<WorkoutAdapter.WorkoutV
public interface WorkoutAdapterListener{ public interface WorkoutAdapterListener{
void onItemClick(Workout workout); void onItemClick(Workout workout);
void onItemLongClick(int pos, Workout workout);
} }

View File

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#FFFFFF"
android:alpha="0.8">
<path
android:fillColor="#FF000000"
android:pathData="M20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94V1h-2v2.06C6.83,3.52 3.52,6.83 3.06,11H1v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94V23h2v-2.06c4.17,-0.46 7.48,-3.77 7.94,-7.94H23v-2h-2.06zM12,19c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 B

View File

@ -197,4 +197,34 @@
</LinearLayout> </LinearLayout>
<FrameLayout
android:id="@+id/recorderWaitingOverlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_red_light">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/imageView3"
android:layout_width="match_parent"
android:layout_height="200dp"
android:src="@drawable/ic_gps_no_fix" />
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:text="@string/waiting_gps"
android:textAlignment="center"
android:textColor="@android:color/background_light"
android:textSize="30sp" />
</LinearLayout>
</FrameLayout>
</RelativeLayout> </RelativeLayout>

View File

@ -18,16 +18,25 @@
~ along with this program. If not, see <http://www.gnu.org/licenses/>. ~ along with this program. If not, see <http://www.gnu.org/licenses/>.
--> -->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/showWorkoutSceneRoot"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".activity.ShowWorkoutActivity"> tools:context=".activity.ShowWorkoutActivity">
<ScrollView
android:id="@+id/showWorkoutDefaultSceneRoot"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout <LinearLayout
android:id="@+id/showWorkoutRoot" android:id="@+id/showWorkoutRoot"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:padding="20dp"></LinearLayout> android:padding="20dp"></LinearLayout>
</ScrollView> </ScrollView>
</LinearLayout>

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
~
~ This file is part of FitoTrack
~
~ FitoTrack is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ FitoTrack is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/showWorkoutMapRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".activity.ShowWorkoutMapActivity">
<LinearLayout
android:id="@+id/showWorkoutMapParent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
~
~ This file is part of FitoTrack
~
~ FitoTrack is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ FitoTrack is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/showWorkoutMapRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".activity.ShowWorkoutMapActivity" >
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintGuide_percent="0.6"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/showWorkoutMapParent"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintBottom_toTopOf="@+id/guideline4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</LinearLayout>
<LinearLayout
android:id="@+id/showWorkoutDiagramParent"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline4"></LinearLayout>
</android.support.constraint.ConstraintLayout>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
~
~ This file is part of FitoTrack
~
~ FitoTrack is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ FitoTrack is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<resources>
<string-array name="pref_map_layers">
<item>Mapnik\nStandard OSM Kartenstil</item>
<item>Humanitarian\nSchöne Karte, aber manchmal nicht verfügbar</item>
<item>Outdoors\nLimitierter Zugriff</item>
<item>Cyclemap\nLimitierter Zugriff</item>
</string-array>
</resources>

View File

@ -94,4 +94,6 @@
<string name="noGpsMessage">Bitte aktiviere GPS, damit dein Workout aufgezeichnet werden kann.</string> <string name="noGpsMessage">Bitte aktiviere GPS, damit dein Workout aufgezeichnet werden kann.</string>
<string name="workoutAscent">Aufstieg</string> <string name="workoutAscent">Aufstieg</string>
<string name="workoutDescent">Abstieg</string> <string name="workoutDescent">Abstieg</string>
<string name="data">Daten</string>
<string name="mapStyle">Kartenstil</string>
</resources> </resources>

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
~
~ This file is part of FitoTrack
~
~ FitoTrack is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ FitoTrack is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<resources>
<string-array name="pref_map_layers">
<item>Mapnik\nDefault OSM Mapstyle</item>
<item>Humanitarian\nClean map but sometimes not available</item>
<item>Outdoors\nLimited access</item>
<item>Cyclemap\nLimited access</item>
</string-array>
<string-array name="pref_map_layers_values">
<item>osm.mapnik</item>
<item>osm.humanitarian</item>
<item>thunderforest.outdoors</item>
<item>thunderforest.cycle</item>
</string-array>
</resources>

View File

@ -112,4 +112,7 @@
<string name="importBackup">Import Data Backup</string> <string name="importBackup">Import Data Backup</string>
<string name="importBackupSummary">Restore a taken backup</string> <string name="importBackupSummary">Restore a taken backup</string>
<string name="gps">GPS</string> <string name="gps">GPS</string>
<string name="data">Data</string>
<string name="mapStyle">Map Style</string>
<string name="waiting_gps">Waiting for GPS</string>
</resources> </resources>

View File

@ -22,7 +22,7 @@
<string-array name="pref_unit_systems"> <string-array name="pref_unit_systems">
<item>Metric</item> <item>Metric</item>
<item>Metric Physical (m/s)</item> <item>Metric with m/s</item>
<item>Imperial</item> <item>Imperial</item>
<item>Imperial with meters</item> <item>Imperial with meters</item>
</string-array> </string-array>

View File

@ -1,11 +1,31 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
~
~ This file is part of FitoTrack
~
~ FitoTrack is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ FitoTrack is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<ListPreference <ListPreference
android:entries="@array/pref_unit_systems" android:entries="@array/pref_unit_systems"
android:entryValues="@array/pref_unit_system_values" android:entryValues="@array/pref_unit_system_values"
android:key="unitSystem" android:key="unitSystem"
android:title="@string/pref_unit_system" /> android:title="@string/pref_unit_system"
android:defaultValue="1" />
<Preference <Preference
android:key="weight" android:key="weight"
@ -14,6 +34,16 @@
android:summary="@string/pref_weight_summary" android:summary="@string/pref_weight_summary"
android:title="@string/pref_weight" /> android:title="@string/pref_weight" />
<ListPreference
android:defaultValue="osm.mapnik"
android:entries="@array/pref_map_layers"
android:entryValues="@array/pref_map_layers_values"
android:key="mapStyle"
android:title="@string/mapStyle" />
<PreferenceCategory android:title="@string/data">
<Preference <Preference
android:key="import" android:key="import"
android:summary="@string/importBackupSummary" android:summary="@string/importBackupSummary"
@ -22,6 +52,7 @@
android:key="export" android:key="export"
android:summary="@string/exportDataSummary" android:summary="@string/exportDataSummary"
android:title="@string/exportData" /> android:title="@string/exportData" />
</PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>

View File

@ -0,0 +1,6 @@
**2.0:**
- Screen to compare height/elevation diagram and the map
- Show overlay if GPS signal wasn't found yet
- Set preferred map layer (#15)
- Speed up data import