mirror of
https://github.com/russok/FitoTrack.git
synced 2025-10-29 00:32:11 -07:00
Better layout for workout map and diagrams
This commit is contained in:
parent
ec73699411
commit
b8a0012f7d
@ -22,13 +22,14 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
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.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
@ -36,8 +37,13 @@
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme"
|
||||
android:appCategory="productivity"
|
||||
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
|
||||
android:name=".activity.ShowWorkoutActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
@ -56,15 +62,17 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="de.tadris.fitness.fileprovider"
|
||||
android:grantUriPermissions="true"
|
||||
android:exported="false">
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/filepaths" />
|
||||
</provider>
|
||||
|
||||
<service android:name=".recording.LocationListener" />
|
||||
<service android:name=".recording.PressureService" />
|
||||
</application>
|
||||
|
||||
@ -20,101 +20,42 @@
|
||||
package de.tadris.fitness.activity;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Typeface;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.transition.Scene;
|
||||
import android.transition.TransitionManager;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
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.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import de.tadris.fitness.Instance;
|
||||
import de.tadris.fitness.R;
|
||||
import de.tadris.fitness.data.Workout;
|
||||
import de.tadris.fitness.data.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.unit.UnitUtils;
|
||||
import de.tadris.fitness.view.ProgressDialogController;
|
||||
|
||||
public class ShowWorkoutActivity extends FitoTrackActivity {
|
||||
static Workout selectedWorkout;
|
||||
public class ShowWorkoutActivity extends WorkoutActivity {
|
||||
|
||||
List<WorkoutSample> samples;
|
||||
Workout workout;
|
||||
ViewGroup root;
|
||||
Resources.Theme theme;
|
||||
MapView map;
|
||||
TileDownloadLayer downloadLayer;
|
||||
FixedPixelCircle highlightingCircle;
|
||||
Handler mHandler= new Handler();
|
||||
|
||||
Scene sceneOverview;
|
||||
Scene speedDiagramScene;
|
||||
Scene heightDiagramScene;
|
||||
Scene mapScene;
|
||||
LineChart speedDiagram;
|
||||
LineChart heightDiagram;
|
||||
ViewGroup sceneRoot;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
workout= selectedWorkout;
|
||||
samples= Arrays.asList(Instance.getInstance(this).db.workoutDao().getAllSamplesOfWorkout(workout.id));
|
||||
setTheme(ThemeManager.getThemeByWorkout(workout));
|
||||
initBeforeContent();
|
||||
|
||||
setContentView(R.layout.activity_show_workout);
|
||||
|
||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
setTitle(WorkoutTypeCalculator.getType(workout));
|
||||
|
||||
theme= getTheme();
|
||||
|
||||
root= findViewById(R.id.showWorkoutRoot);
|
||||
sceneRoot= findViewById(R.id.showWorkoutSceneRoot);
|
||||
ViewGroup sceneLayout= findViewById(R.id.showWorkoutDefaultSceneRoot);
|
||||
sceneOverview= new Scene(sceneRoot, sceneLayout);
|
||||
|
||||
initAfterContent();
|
||||
|
||||
addText(getString(R.string.comment) + ": " + workout.comment).setOnClickListener(v -> {
|
||||
TextView textView= (TextView)v;
|
||||
@ -134,6 +75,9 @@ public class ShowWorkoutActivity extends FitoTrackActivity {
|
||||
|
||||
addMap();
|
||||
|
||||
map.setClickable(false);
|
||||
mapRoot.setOnClickListener(v -> startActivity(new Intent(ShowWorkoutActivity.this, ShowWorkoutMapActivity.class)));
|
||||
|
||||
addTitle(getString(R.string.workoutSpeed));
|
||||
|
||||
addKeyValue(getString(R.string.workoutAvgSpeed), UnitUtils.getSpeed(workout.avgSpeed),
|
||||
@ -141,6 +85,8 @@ public class ShowWorkoutActivity extends FitoTrackActivity {
|
||||
|
||||
addSpeedDiagram();
|
||||
|
||||
speedDiagram.setOnClickListener(v -> startDiagramActivity(ShowWorkoutMapDiagramActivity.DIAGRAM_TYPE_SPEED));
|
||||
|
||||
addTitle(getString(R.string.workoutBurnedEnergy));
|
||||
addKeyValue(getString(R.string.workoutTotalEnergy), workout.calorie + " kcal",
|
||||
getString(R.string.workoutEnergyConsumption), UnitUtils.getRelativeEnergyConsumption((double)workout.calorie / ((double)workout.length / 1000)));
|
||||
@ -152,23 +98,16 @@ public class ShowWorkoutActivity extends FitoTrackActivity {
|
||||
|
||||
addHeightDiagram();
|
||||
|
||||
createScenes();
|
||||
heightDiagram.setOnClickListener(v -> startDiagramActivity(ShowWorkoutMapDiagramActivity.DIAGRAM_TYPE_HEIGHT));
|
||||
|
||||
|
||||
}
|
||||
|
||||
void createScenes(){
|
||||
speedDiagramScene= createDiagramScene(speedDiagram);
|
||||
heightDiagramScene= createDiagramScene(heightDiagram);
|
||||
mapScene= new Scene(sceneRoot, map);
|
||||
void startDiagramActivity(String diagramType){
|
||||
ShowWorkoutMapDiagramActivity.DIAGRAM_TYPE= diagramType;
|
||||
startActivity(new Intent(ShowWorkoutActivity.this, ShowWorkoutMapDiagramActivity.class));
|
||||
}
|
||||
|
||||
Scene createDiagramScene(LineChart chart){
|
||||
LinearLayout layout= new LinearLayout(this);
|
||||
layout.setOrientation(LinearLayout.VERTICAL);
|
||||
layout.addView(map, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getMapHeight()));
|
||||
layout.addView(chart, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
return new Scene(sceneRoot, layout);
|
||||
}
|
||||
|
||||
void openEditCommentDialog(final TextView change){
|
||||
final EditText editText= new EditText(this);
|
||||
@ -235,219 +174,6 @@ public class ShowWorkoutActivity extends FitoTrackActivity {
|
||||
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();
|
||||
|
||||
converter.afterAdd(chart);
|
||||
|
||||
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 afterAdd(LineChart chart);
|
||||
}
|
||||
|
||||
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;
|
||||
heightDiagram.setOnClickListener(v -> TransitionManager.go(heightDiagramScene));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
heightDiagram.setOnClickListener(v -> TransitionManager.go(speedDiagramScene));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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, 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.setOnClickListener(v -> goToMapScene());
|
||||
|
||||
goToMapScene();
|
||||
}
|
||||
|
||||
private void goToMapScene(){
|
||||
TransitionManager.go(mapScene);
|
||||
map.setOnClickListener(null);
|
||||
}
|
||||
|
||||
private void leaveMapScene(){
|
||||
TransitionManager.go(sceneOverview);
|
||||
map.setOnClickListener(v -> goToMapScene());
|
||||
}
|
||||
|
||||
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 onCreateOptionsMenu(Menu menu) {
|
||||
@ -500,9 +226,6 @@ public class ShowWorkoutActivity extends FitoTrackActivity {
|
||||
case R.id.actionExportGpx:
|
||||
exportToGpx();
|
||||
return true;
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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(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();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
37
app/src/main/res/layout/activity_show_workout_map.xml
Normal file
37
app/src/main/res/layout/activity_show_workout_map.xml
Normal 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>
|
||||
@ -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>
|
||||
Loading…
x
Reference in New Issue
Block a user