mirror of
https://github.com/russok/FitoTrack.git
synced 2025-10-30 09:12:11 -07:00
Compare commits
No commits in common. "master" and "v3.0" have entirely different histories.
18
.gitignore
vendored
18
.gitignore
vendored
@ -1,18 +0,0 @@
|
|||||||
# testing
|
|
||||||
/coverage
|
|
||||||
|
|
||||||
# production
|
|
||||||
/build
|
|
||||||
/app/build
|
|
||||||
/app/release
|
|
||||||
|
|
||||||
# misc
|
|
||||||
.*
|
|
||||||
!.gitignore
|
|
||||||
|
|
||||||
# idea
|
|
||||||
*.iml
|
|
||||||
|
|
||||||
# gradle
|
|
||||||
local.properties
|
|
||||||
|
|
||||||
24
NOTICE.md
24
NOTICE.md
@ -49,30 +49,6 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
||||||
<https://github.com/vectorstofinal/geoid_heights>
|
|
||||||
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015 vectorstofinal
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
||||||
<https://github.com/westnordost/osmapi>
|
<https://github.com/westnordost/osmapi>
|
||||||
|
|
||||||
© 2016-2019 Tobias Zwick. This library is released under the terms of the GNU Lesser General Public License (LGPL).
|
© 2016-2019 Tobias Zwick. This library is released under the terms of the GNU Lesser General Public License (LGPL).
|
||||||
|
|||||||
@ -20,7 +20,7 @@ FitoTrack is a mobile app for logging and viewing your workouts. Whether you're
|
|||||||
- **View your workouts.** View general information such as date, time, duration, distance, speed and pace. See your route on a map. Work out your level of performance from the speed diagram.
|
- **View your workouts.** View general information such as date, time, duration, distance, speed and pace. See your route on a map. Work out your level of performance from the speed diagram.
|
||||||
- **Open-Source.** There is neither advertivesment nor tracking, and the source code is open and licensed under the GPLv3.
|
- **Open-Source.** There is neither advertivesment nor tracking, and the source code is open and licensed under the GPLv3.
|
||||||
|
|
||||||
Please see the [Userguide](https://codeberg.org/jannis/FitoTrack/wiki/How-to-use) if you have any problems.
|
see the [Feature-List](https://codeberg.org/jannis/FitoTrack/wiki/Features) for a detailed list of features and the [Roadmap](https://codeberg.org/jannis/FitoTrack/wiki/Roadmap) for planned ones.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -35,8 +35,8 @@ android {
|
|||||||
applicationId "de.tadris.fitness"
|
applicationId "de.tadris.fitness"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 28
|
targetSdkVersion 28
|
||||||
versionCode 500
|
versionCode 300
|
||||||
versionName "5.0"
|
versionName "3.0"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
@ -49,12 +49,6 @@ android {
|
|||||||
sourceCompatibility = '1.8'
|
sourceCompatibility = '1.8'
|
||||||
targetCompatibility = '1.8'
|
targetCompatibility = '1.8'
|
||||||
}
|
}
|
||||||
lintOptions {
|
|
||||||
checkReleaseBuilds false
|
|
||||||
// Or, if you prefer, you can continue to check for errors in release builds,
|
|
||||||
// but continue the build even when errors are found:
|
|
||||||
// abortOnError false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -73,7 +67,7 @@ dependencies {
|
|||||||
implementation 'org.mapsforge:mapsforge-map-android:0.11.0'
|
implementation 'org.mapsforge:mapsforge-map-android:0.11.0'
|
||||||
implementation 'com.caverock:androidsvg:1.3'
|
implementation 'com.caverock:androidsvg:1.3'
|
||||||
|
|
||||||
// UI
|
// Charts
|
||||||
implementation 'net.sf.kxml:kxml2:2.3.0'
|
implementation 'net.sf.kxml:kxml2:2.3.0'
|
||||||
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
|
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
|
||||||
implementation 'com.github.clans:fab:1.6.4'
|
implementation 'com.github.clans:fab:1.6.4'
|
||||||
@ -82,11 +76,8 @@ dependencies {
|
|||||||
implementation 'stax:stax-api:1.0.1'
|
implementation 'stax:stax-api:1.0.1'
|
||||||
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.8'
|
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.8'
|
||||||
|
|
||||||
// File Utils
|
|
||||||
implementation 'commons-io:commons-io:2.6'
|
|
||||||
|
|
||||||
// Upload to OSM
|
// Upload to OSM
|
||||||
implementation('de.westnordost:osmapi-traces:1.0')
|
implementation ('de.westnordost:osmapi-traces:1.0')
|
||||||
configurations {
|
configurations {
|
||||||
compile.exclude group: 'net.sf.kxml', module: 'kxml2' // already included in Android
|
compile.exclude group: 'net.sf.kxml', module: 'kxml2' // already included in Android
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
~
|
~
|
||||||
~ This file is part of FitoTrack
|
~ This file is part of FitoTrack
|
||||||
~
|
~
|
||||||
@ -23,38 +23,32 @@
|
|||||||
package="de.tadris.fitness">
|
package="de.tadris.fitness">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
|
||||||
<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.WRITE_EXTERNAL_STORAGE" />
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:appCategory="productivity"
|
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
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.VoiceAnnouncementsSettingsActivity" />
|
<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.SettingsActivity" />
|
||||||
<activity android:name=".activity.EnterWorkoutActivity"/>
|
|
||||||
<activity
|
|
||||||
android:name=".activity.ShowWorkoutMapActivity"
|
|
||||||
android:screenOrientation="portrait" />
|
|
||||||
<activity
|
|
||||||
android:name=".activity.ShowWorkoutMapDiagramActivity"
|
|
||||||
android:screenOrientation="portrait" />
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.ShowWorkoutActivity"
|
android:name=".activity.ShowWorkoutActivity"
|
||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.RecordWorkoutActivity"
|
android:name=".activity.RecordWorkoutActivity"
|
||||||
android:showOnLockScreen="true"
|
|
||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.ListWorkoutsActivity"
|
android:name=".activity.ListWorkoutsActivity"
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -32,12 +32,11 @@ import java.util.List;
|
|||||||
import de.tadris.fitness.data.AppDatabase;
|
import de.tadris.fitness.data.AppDatabase;
|
||||||
import de.tadris.fitness.data.UserPreferences;
|
import de.tadris.fitness.data.UserPreferences;
|
||||||
import de.tadris.fitness.recording.LocationListener;
|
import de.tadris.fitness.recording.LocationListener;
|
||||||
import de.tadris.fitness.util.FitoTrackThemes;
|
|
||||||
import de.tadris.fitness.util.unit.UnitUtils;
|
import de.tadris.fitness.util.unit.UnitUtils;
|
||||||
|
|
||||||
public class Instance {
|
public class Instance {
|
||||||
|
|
||||||
private static final String DATABASE_NAME = "fito-track";
|
public static final String DATABASE_NAME= "fito-track";
|
||||||
|
|
||||||
private static Instance instance;
|
private static Instance instance;
|
||||||
|
|
||||||
@ -48,22 +47,20 @@ public class Instance {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final AppDatabase db;
|
public AppDatabase db;
|
||||||
public final List<LocationListener.LocationChangeListener> locationChangeListeners = new ArrayList<>();
|
public List<LocationListener.LocationChangeListener> locationChangeListeners= new ArrayList<>();
|
||||||
public final UserPreferences userPreferences;
|
public UserPreferences userPreferences;
|
||||||
public final FitoTrackThemes themes;
|
|
||||||
|
|
||||||
public boolean pressureAvailable= false;
|
public boolean pressureAvailable= false;
|
||||||
public float lastPressure= 0;
|
public float lastPressure= 0;
|
||||||
|
|
||||||
private Instance(Context context) {
|
private Instance(Context context) {
|
||||||
userPreferences= new UserPreferences(context);
|
userPreferences= new UserPreferences(context);
|
||||||
themes = new FitoTrackThemes(context);
|
|
||||||
db = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, DATABASE_NAME)
|
db = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, DATABASE_NAME)
|
||||||
.addMigrations(new Migration(1, 2) {
|
.addMigrations(new Migration(1, 2) {
|
||||||
@Override
|
@Override
|
||||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||||
try {
|
try{
|
||||||
database.beginTransaction();
|
database.beginTransaction();
|
||||||
|
|
||||||
database.execSQL("ALTER table workout add descent REAL NOT NULL DEFAULT 0;");
|
database.execSQL("ALTER table workout add descent REAL NOT NULL DEFAULT 0;");
|
||||||
@ -88,20 +85,7 @@ public class Instance {
|
|||||||
database.execSQL("DROP TABLE workout_sample2");
|
database.execSQL("DROP TABLE workout_sample2");
|
||||||
|
|
||||||
database.setTransactionSuccessful();
|
database.setTransactionSuccessful();
|
||||||
} finally {
|
}finally {
|
||||||
database.endTransaction();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, new Migration(2, 3) {
|
|
||||||
@Override
|
|
||||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
|
||||||
try {
|
|
||||||
database.beginTransaction();
|
|
||||||
|
|
||||||
database.execSQL("ALTER table workout add COLUMN edited INTEGER not null default 0");
|
|
||||||
|
|
||||||
database.setTransactionSuccessful();
|
|
||||||
} finally {
|
|
||||||
database.endTransaction();
|
database.endTransaction();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,221 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
*
|
|
||||||
* This file is part of FitoTrack
|
|
||||||
*
|
|
||||||
* FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.tadris.fitness.activity;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.text.InputType;
|
|
||||||
import android.util.TypedValue;
|
|
||||||
import android.view.KeyEvent;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.inputmethod.EditorInfo;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Calendar;
|
|
||||||
|
|
||||||
import de.tadris.fitness.R;
|
|
||||||
import de.tadris.fitness.data.WorkoutBuilder;
|
|
||||||
import de.tadris.fitness.data.WorkoutType;
|
|
||||||
import de.tadris.fitness.dialog.DatePickerFragment;
|
|
||||||
import de.tadris.fitness.dialog.DurationPickerDialogFragment;
|
|
||||||
import de.tadris.fitness.dialog.SelectWorkoutTypeDialog;
|
|
||||||
import de.tadris.fitness.dialog.TimePickerFragment;
|
|
||||||
import de.tadris.fitness.util.unit.UnitUtils;
|
|
||||||
|
|
||||||
public class EnterWorkoutActivity extends InformationActivity implements SelectWorkoutTypeDialog.WorkoutTypeSelectListener, DatePickerFragment.DatePickerCallback, TimePickerFragment.TimePickerCallback, DurationPickerDialogFragment.DurationPickListener {
|
|
||||||
|
|
||||||
WorkoutBuilder workoutBuilder = new WorkoutBuilder();
|
|
||||||
TextView typeTextView, dateTextView, timeTextView, durationTextView;
|
|
||||||
EditText distanceEditText, commentEditText;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setContentView(R.layout.activity_enter_workout);
|
|
||||||
|
|
||||||
initRoot();
|
|
||||||
|
|
||||||
addTitle(getString(R.string.info));
|
|
||||||
setupActionBar();
|
|
||||||
|
|
||||||
KeyValueLine typeLine = addKeyValueLine(getString(R.string.type));
|
|
||||||
typeTextView = typeLine.value;
|
|
||||||
typeLine.lineRoot.setOnClickListener(v -> showTypeSelection());
|
|
||||||
|
|
||||||
distanceEditText = new EditText(this);
|
|
||||||
distanceEditText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
|
|
||||||
distanceEditText.setSingleLine(true);
|
|
||||||
distanceEditText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
|
|
||||||
distanceEditText.setOnEditorActionListener((v, actionId, event) -> {
|
|
||||||
// If the User clicks on the finish button on the keyboard, continue by showing the date selection
|
|
||||||
if (actionId == EditorInfo.IME_ACTION_SEARCH ||
|
|
||||||
actionId == EditorInfo.IME_ACTION_DONE ||
|
|
||||||
event != null &&
|
|
||||||
event.getAction() == KeyEvent.ACTION_DOWN &&
|
|
||||||
event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
|
|
||||||
if (event == null || !event.isShiftPressed()) {
|
|
||||||
showDateSelection();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
addKeyValueLine(getString(R.string.workoutDistance), distanceEditText, UnitUtils.CHOSEN_SYSTEM.getLongDistanceUnit());
|
|
||||||
|
|
||||||
|
|
||||||
KeyValueLine dateLine = addKeyValueLine(getString(R.string.workoutDate));
|
|
||||||
dateLine.lineRoot.setOnClickListener(v -> showDateSelection());
|
|
||||||
dateTextView = dateLine.value;
|
|
||||||
|
|
||||||
KeyValueLine timeLine = addKeyValueLine(getString(R.string.workoutStartTime));
|
|
||||||
timeLine.lineRoot.setOnClickListener(v -> showTimeSelection());
|
|
||||||
timeTextView = timeLine.value;
|
|
||||||
|
|
||||||
KeyValueLine durationLine = addKeyValueLine(getString(R.string.workoutDuration));
|
|
||||||
durationLine.lineRoot.setOnClickListener(v -> showDurationSelection());
|
|
||||||
durationTextView = durationLine.value;
|
|
||||||
|
|
||||||
addTitle(getString(R.string.comment));
|
|
||||||
|
|
||||||
commentEditText = new EditText(this);
|
|
||||||
commentEditText.setSingleLine(true);
|
|
||||||
root.addView(commentEditText);
|
|
||||||
|
|
||||||
updateTextViews();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveWorkout() {
|
|
||||||
workoutBuilder.setComment(commentEditText.getText().toString());
|
|
||||||
try {
|
|
||||||
workoutBuilder.setLength((int) (Double.parseDouble(distanceEditText.getText().toString()) * 1000));
|
|
||||||
} catch (NumberFormatException ignored) {
|
|
||||||
distanceEditText.requestFocus();
|
|
||||||
distanceEditText.setError(getString(R.string.errorEnterValidNumber));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (workoutBuilder.getStart().getTimeInMillis() > System.currentTimeMillis()) {
|
|
||||||
Toast.makeText(this, R.string.errorWorkoutAddFuture, Toast.LENGTH_LONG).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (workoutBuilder.getDuration() < 1000) {
|
|
||||||
Toast.makeText(this, R.string.errorEnterValidDuration, Toast.LENGTH_LONG).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ShowWorkoutActivity.selectedWorkout = workoutBuilder.insertWorkout(this);
|
|
||||||
startActivity(new Intent(this, ShowWorkoutActivity.class));
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateTextViews() {
|
|
||||||
typeTextView.setText(getString(workoutBuilder.getWorkoutType().title));
|
|
||||||
dateTextView.setText(SimpleDateFormat.getDateInstance().format(workoutBuilder.getStart().getTime()));
|
|
||||||
timeTextView.setText(SimpleDateFormat.getTimeInstance().format(workoutBuilder.getStart().getTime()));
|
|
||||||
durationTextView.setText(UnitUtils.getHourMinuteSecondTime(workoutBuilder.getDuration()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showTypeSelection() {
|
|
||||||
new SelectWorkoutTypeDialog(this, this).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSelectWorkoutType(WorkoutType workoutType) {
|
|
||||||
workoutBuilder.setWorkoutType(workoutType);
|
|
||||||
updateTextViews();
|
|
||||||
distanceEditText.requestFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showDateSelection() {
|
|
||||||
DatePickerFragment fragment = new DatePickerFragment();
|
|
||||||
fragment.callback = this;
|
|
||||||
fragment.show(getFragmentManager(), "datePicker");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDatePick(int year, int month, int day) {
|
|
||||||
workoutBuilder.getStart().set(year, month, day);
|
|
||||||
updateTextViews();
|
|
||||||
showTimeSelection();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showTimeSelection() {
|
|
||||||
TimePickerFragment fragment = new TimePickerFragment();
|
|
||||||
fragment.callback = this;
|
|
||||||
fragment.show(getFragmentManager(), "timePicker");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTimePick(int hour, int minute) {
|
|
||||||
workoutBuilder.getStart().set(Calendar.HOUR_OF_DAY, hour);
|
|
||||||
workoutBuilder.getStart().set(Calendar.MINUTE, minute);
|
|
||||||
workoutBuilder.getStart().set(Calendar.SECOND, 0);
|
|
||||||
updateTextViews();
|
|
||||||
showDurationSelection();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showDurationSelection() {
|
|
||||||
DurationPickerDialogFragment fragment = new DurationPickerDialogFragment(this, this, workoutBuilder.getDuration());
|
|
||||||
fragment.listener = this;
|
|
||||||
fragment.initialDuration = workoutBuilder.getDuration();
|
|
||||||
fragment.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDurationPick(long duration) {
|
|
||||||
workoutBuilder.setDuration(duration);
|
|
||||||
updateTextViews();
|
|
||||||
commentEditText.requestFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
// Inflate the menu; this adds items to the action bar if it is present.
|
|
||||||
getMenuInflater().inflate(R.menu.enter_workout_menu, menu);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
int id = item.getItemId();
|
|
||||||
switch (id) {
|
|
||||||
case R.id.actionEnterWorkoutAdd:
|
|
||||||
saveWorkout();
|
|
||||||
return true;
|
|
||||||
case android.R.id.home:
|
|
||||||
finish();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupActionBar() {
|
|
||||||
if (getActionBar() != null) {
|
|
||||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void initRoot() {
|
|
||||||
root = findViewById(R.id.enterWorkoutRoot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -19,35 +19,48 @@
|
|||||||
|
|
||||||
package de.tadris.fitness.activity;
|
package de.tadris.fitness.activity;
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.core.app.ActivityCompat;
|
|
||||||
|
|
||||||
import de.tadris.fitness.Instance;
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
import de.tadris.fitness.R;
|
import de.tadris.fitness.R;
|
||||||
|
|
||||||
abstract public class FitoTrackActivity extends Activity {
|
abstract public class FitoTrackActivity extends Activity {
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setTheme(Instance.getInstance(this).themes.getDefaultTheme());
|
|
||||||
}
|
|
||||||
|
|
||||||
int getThemePrimaryColor() {
|
|
||||||
|
protected int getThemePrimaryColor() {
|
||||||
final TypedValue value = new TypedValue ();
|
final TypedValue value = new TypedValue ();
|
||||||
getTheme().resolveAttribute (android.R.attr.colorPrimary, value, true);
|
getTheme().resolveAttribute (android.R.attr.colorPrimary, value, true);
|
||||||
return value.data;
|
return value.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void showErrorDialog(Exception e, @StringRes int title, @StringRes int message) {
|
protected void shareFile(Uri uri){
|
||||||
|
Intent intentShareFile = new Intent(Intent.ACTION_SEND);
|
||||||
|
intentShareFile.setDataAndType(uri, getContentResolver().getType(uri));
|
||||||
|
intentShareFile.putExtra(Intent.EXTRA_STREAM, uri);
|
||||||
|
intentShareFile.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
|
||||||
|
startActivity(Intent.createChooser(intentShareFile, getString(R.string.shareFile)));
|
||||||
|
|
||||||
|
Log.d("Export", uri.toString());
|
||||||
|
Log.d("Export", getContentResolver().getType(uri));
|
||||||
|
try {
|
||||||
|
Log.d("Export", new BufferedInputStream(getContentResolver().openInputStream(uri)).toString());
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void showErrorDialog(Exception e, @StringRes int title, @StringRes int message){
|
||||||
new AlertDialog.Builder(this)
|
new AlertDialog.Builder(this)
|
||||||
.setTitle(title)
|
.setTitle(title)
|
||||||
.setMessage(getString(message) + "\n\n" + e.getMessage())
|
.setMessage(getString(message) + "\n\n" + e.getMessage())
|
||||||
@ -55,16 +68,5 @@ abstract public class FitoTrackActivity extends Activity {
|
|||||||
.create().show();
|
.create().show();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void requestStoragePermissions() {
|
|
||||||
if (!hasStoragePermission()) {
|
|
||||||
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean hasStoragePermission() {
|
|
||||||
return ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED &&
|
|
||||||
ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,135 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
*
|
|
||||||
* This file is part of FitoTrack
|
|
||||||
*
|
|
||||||
* FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.tadris.fitness.activity;
|
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.media.Ringtone;
|
|
||||||
import android.media.RingtoneManager;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.preference.ListPreference;
|
|
||||||
import android.preference.Preference;
|
|
||||||
import android.preference.PreferenceActivity;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.preference.RingtonePreference;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.annotation.StringRes;
|
|
||||||
|
|
||||||
import de.tadris.fitness.Instance;
|
|
||||||
import de.tadris.fitness.R;
|
|
||||||
import de.tadris.fitness.util.unit.UnitUtils;
|
|
||||||
|
|
||||||
public abstract class FitoTrackSettingsActivity extends PreferenceActivity {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
|
||||||
setTheme(Instance.getInstance(this).themes.getDefaultTheme());
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void showErrorDialog(Exception e, @StringRes int title, @StringRes int message) {
|
|
||||||
new AlertDialog.Builder(this)
|
|
||||||
.setTitle(title)
|
|
||||||
.setMessage(getString(message) + "\n\n" + e.getMessage())
|
|
||||||
.setPositiveButton(R.string.okay, null)
|
|
||||||
.create().show();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A preference value change listener that updates the preference's summary
|
|
||||||
* to reflect its new value.
|
|
||||||
*/
|
|
||||||
protected static final Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = (preference, value) -> {
|
|
||||||
String stringValue = value.toString();
|
|
||||||
|
|
||||||
if (preference instanceof ListPreference) {
|
|
||||||
// For list preferences, look up the correct display value in
|
|
||||||
// the preference's 'entries' list.
|
|
||||||
ListPreference listPreference = (ListPreference) preference;
|
|
||||||
int index = listPreference.findIndexOfValue(stringValue);
|
|
||||||
|
|
||||||
// Set the summary to reflect the new value.
|
|
||||||
preference.setSummary(
|
|
||||||
index >= 0
|
|
||||||
? listPreference.getEntries()[index]
|
|
||||||
: null);
|
|
||||||
|
|
||||||
} else if (preference instanceof RingtonePreference) {
|
|
||||||
// For ringtone preferences, look up the correct display value
|
|
||||||
// using RingtoneManager.
|
|
||||||
if (TextUtils.isEmpty(stringValue)) {
|
|
||||||
// Empty values correspond to 'silent' (no ringtone).
|
|
||||||
preference.setSummary(R.string.pref_ringtone_silent);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Ringtone ringtone = RingtoneManager.getRingtone(
|
|
||||||
preference.getContext(), Uri.parse(stringValue));
|
|
||||||
|
|
||||||
if (ringtone == null) {
|
|
||||||
// Clear the summary if there was a lookup error.
|
|
||||||
preference.setSummary(null);
|
|
||||||
} else {
|
|
||||||
// Set the summary to reflect the new ringtone display
|
|
||||||
// name.
|
|
||||||
String name = ringtone.getTitle(preference.getContext());
|
|
||||||
preference.setSummary(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// For all other preferences, set the summary to the value's
|
|
||||||
// simple string representation.
|
|
||||||
preference.setSummary(stringValue);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
protected static void bindPreferenceSummaryToValue(Preference preference) {
|
|
||||||
// Set the listener to watch for value changes.
|
|
||||||
preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
|
|
||||||
|
|
||||||
// Trigger the listener immediately with the preference's
|
|
||||||
// current value.
|
|
||||||
sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
|
|
||||||
PreferenceManager
|
|
||||||
.getDefaultSharedPreferences(preference.getContext())
|
|
||||||
.getString(preference.getKey(), ""));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
UnitUtils.setUnit(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onMenuItemSelected(int featureId, MenuItem item) {
|
|
||||||
int id = item.getItemId();
|
|
||||||
if (id == android.R.id.home) {
|
|
||||||
finish();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return super.onMenuItemSelected(featureId, item);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,141 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
*
|
|
||||||
* This file is part of FitoTrack
|
|
||||||
*
|
|
||||||
* FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.tadris.fitness.activity;
|
|
||||||
|
|
||||||
import android.graphics.Typeface;
|
|
||||||
import android.util.TypedValue;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import de.tadris.fitness.R;
|
|
||||||
|
|
||||||
public abstract class InformationActivity extends FitoTrackActivity {
|
|
||||||
|
|
||||||
ViewGroup root;
|
|
||||||
|
|
||||||
protected void addTitle(String title) {
|
|
||||||
TextView textView = new TextView(this);
|
|
||||||
textView.setText(title);
|
|
||||||
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
|
|
||||||
textView.setTextColor(getThemePrimaryColor());
|
|
||||||
textView.setTypeface(Typeface.DEFAULT_BOLD);
|
|
||||||
textView.setAllCaps(true);
|
|
||||||
textView.setPadding(0, 20, 0, 0);
|
|
||||||
|
|
||||||
root.addView(textView);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected TextView addText(String text, boolean themeColor) {
|
|
||||||
TextView textView = createTextView(text, themeColor);
|
|
||||||
root.addView(textView);
|
|
||||||
|
|
||||||
return textView;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected TextView createTextView(String text) {
|
|
||||||
return createTextView(text, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected TextView createTextView(String text, boolean themeColor) {
|
|
||||||
TextView textView = new TextView(this);
|
|
||||||
textView.setText(text);
|
|
||||||
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
|
|
||||||
if (themeColor) {
|
|
||||||
textView.setTextColor(getThemePrimaryColor());
|
|
||||||
} else {
|
|
||||||
textView.setTextColor(getResources().getColor(R.color.textLighterBlack));
|
|
||||||
}
|
|
||||||
textView.setPadding(0, 20, 0, 0);
|
|
||||||
|
|
||||||
return textView;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void addKeyValue(String key1, String value1) {
|
|
||||||
addKeyValue(key1, value1, "", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void addKeyValue(String key1, String value1, String key2, String value2) {
|
|
||||||
View v = getLayoutInflater().inflate(R.layout.show_entry, root, false);
|
|
||||||
|
|
||||||
TextView title1 = v.findViewById(R.id.v1title);
|
|
||||||
TextView title2 = v.findViewById(R.id.v2title);
|
|
||||||
TextView v1 = v.findViewById(R.id.v1value);
|
|
||||||
TextView v2 = v.findViewById(R.id.v2value);
|
|
||||||
|
|
||||||
title1.setText(key1);
|
|
||||||
title2.setText(key2);
|
|
||||||
v1.setText(value1);
|
|
||||||
v2.setText(value2);
|
|
||||||
|
|
||||||
root.addView(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected KeyValueLine addKeyValueLine(String key) {
|
|
||||||
return addKeyValueLine(key, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected KeyValueLine addKeyValueLine(String key, String value) {
|
|
||||||
return addKeyValueLine(key, null, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected KeyValueLine addKeyValueLine(String key, @Nullable View view) {
|
|
||||||
return addKeyValueLine(key, view, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected KeyValueLine addKeyValueLine(String key, @Nullable View view, String value) {
|
|
||||||
View v = getLayoutInflater().inflate(R.layout.enter_workout_line, root, false);
|
|
||||||
|
|
||||||
TextView keyView = v.findViewById(R.id.lineKey);
|
|
||||||
TextView valueView = v.findViewById(R.id.lineValue);
|
|
||||||
LinearLayout customViewRoot = v.findViewById(R.id.lineViewRoot);
|
|
||||||
|
|
||||||
keyView.setText(key);
|
|
||||||
valueView.setText(value);
|
|
||||||
|
|
||||||
if (view != null) {
|
|
||||||
customViewRoot.addView(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
root.addView(v);
|
|
||||||
return new KeyValueLine(v, keyView, valueView, view);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class KeyValueLine {
|
|
||||||
public View lineRoot;
|
|
||||||
public TextView key;
|
|
||||||
public TextView value;
|
|
||||||
public View customView;
|
|
||||||
|
|
||||||
public KeyValueLine(View lineRoot, TextView key, TextView value, View customView) {
|
|
||||||
this.lineRoot = lineRoot;
|
|
||||||
this.key = key;
|
|
||||||
this.value = value;
|
|
||||||
this.customView = customView;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract void initRoot();
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -42,12 +42,12 @@ public class LauncherActivity extends Activity {
|
|||||||
new Handler().postDelayed(this::init, 100);
|
new Handler().postDelayed(this::init, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init() {
|
void init(){
|
||||||
Instance.getInstance(this);
|
Instance.getInstance(this);
|
||||||
start();
|
start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void start() {
|
void start(){
|
||||||
startActivity(new Intent(this, ListWorkoutsActivity.class));
|
startActivity(new Intent(this, ListWorkoutsActivity.class));
|
||||||
finish();
|
finish();
|
||||||
overridePendingTransition(R.anim.fade_in, R.anim.stay);
|
overridePendingTransition(R.anim.fade_in, R.anim.stay);
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
package de.tadris.fitness.activity;
|
package de.tadris.fitness.activity;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
@ -36,17 +37,16 @@ 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.data.WorkoutType;
|
|
||||||
import de.tadris.fitness.util.DialogUtils;
|
import de.tadris.fitness.util.DialogUtils;
|
||||||
import de.tadris.fitness.view.WorkoutAdapter;
|
import de.tadris.fitness.view.WorkoutAdapter;
|
||||||
|
|
||||||
public class ListWorkoutsActivity extends FitoTrackActivity implements WorkoutAdapter.WorkoutAdapterListener {
|
public class ListWorkoutsActivity extends Activity implements WorkoutAdapter.WorkoutAdapterListener {
|
||||||
|
|
||||||
private RecyclerView listView;
|
private RecyclerView listView;
|
||||||
private RecyclerView.Adapter adapter;
|
private RecyclerView.Adapter adapter;
|
||||||
private RecyclerView.LayoutManager layoutManager;
|
private RecyclerView.LayoutManager layoutManager;
|
||||||
private FloatingActionMenu menu;
|
private FloatingActionMenu menu;
|
||||||
private Workout[] workouts;
|
Workout[] workouts;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -63,21 +63,23 @@ public class ListWorkoutsActivity extends FitoTrackActivity implements WorkoutAd
|
|||||||
menu= findViewById(R.id.workoutListMenu);
|
menu= findViewById(R.id.workoutListMenu);
|
||||||
menu.setOnMenuButtonLongClickListener(v -> {
|
menu.setOnMenuButtonLongClickListener(v -> {
|
||||||
if(workouts.length > 0){
|
if(workouts.length > 0){
|
||||||
startRecording(workouts[0].getWorkoutType());
|
startRecording(workouts[0].workoutType);
|
||||||
return true;
|
return true;
|
||||||
}else{
|
}else{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
findViewById(R.id.workoutListRecordRunning).setOnClickListener(v -> startRecording(WorkoutType.RUNNING));
|
findViewById(R.id.workoutListRecordRunning).setOnClickListener(v -> startRecording(Workout.WORKOUT_TYPE_RUNNING));
|
||||||
findViewById(R.id.workoutListRecordHiking).setOnClickListener(v -> startRecording(WorkoutType.HIKING));
|
findViewById(R.id.workoutListRecordHiking) .setOnClickListener(v -> startRecording(Workout.WORKOUT_TYPE_HIKING));
|
||||||
findViewById(R.id.workoutListRecordCycling).setOnClickListener(v -> startRecording(WorkoutType.CYCLING));
|
findViewById(R.id.workoutListRecordCycling).setOnClickListener(v -> startRecording(Workout.WORKOUT_TYPE_CYCLING));
|
||||||
findViewById(R.id.workoutListEnter).setOnClickListener(v -> startEnterWorkoutActivity());
|
|
||||||
|
loadData();
|
||||||
|
|
||||||
checkFirstStart();
|
checkFirstStart();
|
||||||
|
|
||||||
refresh();
|
refreshAdapter();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkFirstStart(){
|
private void checkFirstStart(){
|
||||||
@ -93,13 +95,7 @@ public class ListWorkoutsActivity extends FitoTrackActivity implements WorkoutAd
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startEnterWorkoutActivity() {
|
public void startRecording(String activity){
|
||||||
menu.close(true);
|
|
||||||
final Intent intent = new Intent(this, EnterWorkoutActivity.class);
|
|
||||||
new Handler().postDelayed(() -> startActivity(intent), 300);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startRecording(WorkoutType activity) {
|
|
||||||
menu.close(true);
|
menu.close(true);
|
||||||
RecordWorkoutActivity.ACTIVITY= activity;
|
RecordWorkoutActivity.ACTIVITY= activity;
|
||||||
final Intent intent= new Intent(this, RecordWorkoutActivity.class);
|
final Intent intent= new Intent(this, RecordWorkoutActivity.class);
|
||||||
@ -110,11 +106,15 @@ public class ListWorkoutsActivity extends FitoTrackActivity implements WorkoutAd
|
|||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
||||||
refresh();
|
int count= workouts.length;
|
||||||
|
loadData();
|
||||||
|
if(count != workouts.length){
|
||||||
|
refreshAdapter();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onItemClick(int pos, Workout workout) {
|
public void onItemClick(Workout workout) {
|
||||||
ShowWorkoutActivity.selectedWorkout= workout;
|
ShowWorkoutActivity.selectedWorkout= workout;
|
||||||
startActivity(new Intent(this, ShowWorkoutActivity.class));
|
startActivity(new Intent(this, ShowWorkoutActivity.class));
|
||||||
}
|
}
|
||||||
@ -123,15 +123,11 @@ public class ListWorkoutsActivity extends FitoTrackActivity implements WorkoutAd
|
|||||||
public void onItemLongClick(int pos, Workout workout) {
|
public void onItemLongClick(int pos, Workout workout) {
|
||||||
DialogUtils.showDeleteWorkoutDialog(this, () -> {
|
DialogUtils.showDeleteWorkoutDialog(this, () -> {
|
||||||
Instance.getInstance(ListWorkoutsActivity.this).db.workoutDao().deleteWorkout(workout);
|
Instance.getInstance(ListWorkoutsActivity.this).db.workoutDao().deleteWorkout(workout);
|
||||||
refresh();
|
loadData();
|
||||||
|
adapter.notifyItemRemoved(pos);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refresh() {
|
|
||||||
loadData();
|
|
||||||
refreshAdapter();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadData(){
|
private void loadData(){
|
||||||
workouts= Instance.getInstance(this).db.workoutDao().getWorkouts();
|
workouts= Instance.getInstance(this).db.workoutDao().getWorkouts();
|
||||||
}
|
}
|
||||||
@ -152,9 +148,10 @@ public class ListWorkoutsActivity extends FitoTrackActivity implements WorkoutAd
|
|||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
int id = item.getItemId();
|
int id = item.getItemId();
|
||||||
|
|
||||||
if (id == R.id.actionOpenSettings) {
|
switch (id){
|
||||||
startActivity(new Intent(this, SettingsActivity.class));
|
case R.id.actionOpenSettings:
|
||||||
return true;
|
startActivity(new Intent(this, SettingsActivity.class));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -35,7 +35,6 @@ 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.view.ViewGroup;
|
||||||
import android.view.WindowManager;
|
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
@ -54,62 +53,54 @@ 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.WorkoutType;
|
import de.tadris.fitness.data.Workout;
|
||||||
import de.tadris.fitness.map.MapManager;
|
import de.tadris.fitness.map.MapManager;
|
||||||
|
import de.tadris.fitness.map.tilesource.TileSources;
|
||||||
import de.tadris.fitness.recording.LocationListener;
|
import de.tadris.fitness.recording.LocationListener;
|
||||||
import de.tadris.fitness.recording.PressureService;
|
import de.tadris.fitness.recording.PressureService;
|
||||||
import de.tadris.fitness.recording.WorkoutRecorder;
|
import de.tadris.fitness.recording.WorkoutRecorder;
|
||||||
import de.tadris.fitness.recording.announcement.AnnouncementGPSStatus;
|
import de.tadris.fitness.util.ThemeManager;
|
||||||
import de.tadris.fitness.recording.announcement.VoiceAnnouncements;
|
|
||||||
import de.tadris.fitness.util.unit.UnitUtils;
|
import de.tadris.fitness.util.unit.UnitUtils;
|
||||||
|
|
||||||
public class RecordWorkoutActivity extends FitoTrackActivity implements LocationListener.LocationChangeListener, WorkoutRecorder.WorkoutRecorderListener, VoiceAnnouncements.VoiceAnnouncementCallback {
|
public class RecordWorkoutActivity extends FitoTrackActivity implements LocationListener.LocationChangeListener, WorkoutRecorder.WorkoutRecorderListener {
|
||||||
|
|
||||||
public static WorkoutType ACTIVITY = WorkoutType.OTHER;
|
public static String ACTIVITY= Workout.WORKOUT_TYPE_RUNNING;
|
||||||
|
|
||||||
private MapView mapView;
|
MapView mapView;
|
||||||
private TileDownloadLayer downloadLayer;
|
TileDownloadLayer downloadLayer;
|
||||||
private WorkoutRecorder recorder;
|
WorkoutRecorder recorder;
|
||||||
private Polyline polyline;
|
Polyline polyline;
|
||||||
private final List<LatLong> latLongList = new ArrayList<>();
|
List<LatLong> latLongList= new ArrayList<>();
|
||||||
private final InfoViewHolder[] infoViews = new InfoViewHolder[4];
|
InfoViewHolder[] infoViews= new InfoViewHolder[4];
|
||||||
private TextView timeView;
|
TextView timeView, gpsStatusView;
|
||||||
private TextView gpsStatusView;
|
View waitingForGPSOverlay;
|
||||||
private TextView attribution;
|
boolean gpsFound= false;
|
||||||
private View waitingForGPSOverlay;
|
boolean isResumed= false;
|
||||||
private boolean gpsFound = false;
|
private Handler mHandler= new Handler();
|
||||||
private boolean isResumed = false;
|
PowerManager.WakeLock wakeLock;
|
||||||
private final Handler mHandler = new Handler();
|
Intent locationListener;
|
||||||
private PowerManager.WakeLock wakeLock;
|
Intent pressureService;
|
||||||
private Intent locationListener;
|
|
||||||
private Intent pressureService;
|
|
||||||
private boolean saved= false;
|
private boolean saved= false;
|
||||||
|
|
||||||
private VoiceAnnouncements voiceAnnouncements;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setTheme(Instance.getInstance(this).themes.getWorkoutTypeTheme(ACTIVITY));
|
setTheme(ThemeManager.getThemeByWorkoutType(ACTIVITY));
|
||||||
setContentView(R.layout.activity_record_workout);
|
setContentView(R.layout.activity_record_workout);
|
||||||
|
|
||||||
setTitle(R.string.recordWorkout);
|
setTitle(R.string.recordWorkout);
|
||||||
|
|
||||||
setupMap();
|
setupMap();
|
||||||
|
|
||||||
((ViewGroup) findViewById(R.id.recordMapViewerRoot)).addView(mapView);
|
((ViewGroup)findViewById(R.id.recordMapViewrRoot)).addView(mapView);
|
||||||
waitingForGPSOverlay= findViewById(R.id.recorderWaitingOverlay);
|
waitingForGPSOverlay= findViewById(R.id.recorderWaitingOverlay);
|
||||||
waitingForGPSOverlay.setVisibility(View.VISIBLE);
|
waitingForGPSOverlay.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
attribution = findViewById(R.id.recordMapAttribution);
|
|
||||||
|
|
||||||
checkPermissions();
|
checkPermissions();
|
||||||
|
|
||||||
recorder= new WorkoutRecorder(this, ACTIVITY, this);
|
recorder= new WorkoutRecorder(this, ACTIVITY, this);
|
||||||
recorder.start();
|
recorder.start();
|
||||||
|
|
||||||
voiceAnnouncements = new VoiceAnnouncements(this, this);
|
|
||||||
|
|
||||||
infoViews[0]= new InfoViewHolder(findViewById(R.id.recordInfo1Title), findViewById(R.id.recordInfo1Value));
|
infoViews[0]= new InfoViewHolder(findViewById(R.id.recordInfo1Title), findViewById(R.id.recordInfo1Value));
|
||||||
infoViews[1]= new InfoViewHolder(findViewById(R.id.recordInfo2Title), findViewById(R.id.recordInfo2Value));
|
infoViews[1]= new InfoViewHolder(findViewById(R.id.recordInfo2Title), findViewById(R.id.recordInfo2Value));
|
||||||
infoViews[2]= new InfoViewHolder(findViewById(R.id.recordInfo3Title), findViewById(R.id.recordInfo3Value));
|
infoViews[2]= new InfoViewHolder(findViewById(R.id.recordInfo3Title), findViewById(R.id.recordInfo3Value));
|
||||||
@ -138,9 +129,7 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
|||||||
waitingForGPSOverlay.clearAnimation();
|
waitingForGPSOverlay.clearAnimation();
|
||||||
waitingForGPSOverlay.animate().alpha(0f).setDuration(1000).setListener(new Animator.AnimatorListener() {
|
waitingForGPSOverlay.animate().alpha(0f).setDuration(1000).setListener(new Animator.AnimatorListener() {
|
||||||
@Override public void onAnimationStart(Animator animator) { }
|
@Override public void onAnimationStart(Animator animator) { }
|
||||||
|
|
||||||
@Override public void onAnimationCancel(Animator animator) { }
|
@Override public void onAnimationCancel(Animator animator) { }
|
||||||
|
|
||||||
@Override public void onAnimationRepeat(Animator animator) { }
|
@Override public void onAnimationRepeat(Animator animator) { }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -148,16 +137,17 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
|||||||
waitingForGPSOverlay.setVisibility(View.GONE);
|
waitingForGPSOverlay.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
hideOSMAttribution();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void hideOSMAttribution() {
|
|
||||||
attribution.animate().alpha(0f).setDuration(1000).setStartDelay(5000).start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupMap(){
|
private void setupMap(){
|
||||||
this.mapView= new MapView(this);
|
this.mapView= new MapView(this);
|
||||||
downloadLayer = MapManager.setupMap(mapView);
|
TileSources.Purpose purpose;
|
||||||
|
if(ACTIVITY.equals(Workout.WORKOUT_TYPE_CYCLING)){
|
||||||
|
purpose= TileSources.Purpose.CYCLING;
|
||||||
|
}else{
|
||||||
|
purpose= TileSources.Purpose.OUTDOOR;
|
||||||
|
}
|
||||||
|
downloadLayer= MapManager.setupMap(mapView, purpose);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateLine(){
|
private void updateLine(){
|
||||||
@ -178,7 +168,9 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
|||||||
try{
|
try{
|
||||||
while (recorder.isActive()){
|
while (recorder.isActive()){
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
mHandler.post(this::updateDescription);
|
if(isResumed){
|
||||||
|
mHandler.post(this::updateDescription);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}catch (InterruptedException e){
|
}catch (InterruptedException e){
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@ -186,23 +178,15 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
|||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int i= 0;
|
||||||
|
|
||||||
private void updateDescription() {
|
private void updateDescription(){
|
||||||
long duration = recorder.getDuration();
|
i++;
|
||||||
int distanceInMeters = recorder.getDistanceInMeters();
|
timeView.setText(UnitUtils.getHourMinuteSecondTime(recorder.getDuration()));
|
||||||
final String distanceCaption = getString(R.string.workoutDistance);
|
infoViews[0].setText(getString(R.string.workoutDistance), UnitUtils.getDistance(recorder.getDistance()));
|
||||||
final String distance = UnitUtils.getDistance(distanceInMeters);
|
infoViews[1].setText(getString(R.string.workoutBurnedEnergy), recorder.getCalories() + " kcal");
|
||||||
final String avgSpeed = UnitUtils.getSpeed(Math.min(100d, recorder.getAvgSpeed()));
|
infoViews[2].setText(getString(R.string.workoutAvgSpeed), UnitUtils.getSpeed(Math.min(100d, recorder.getAvgSpeed())));
|
||||||
if (isResumed) {
|
infoViews[3].setText(getString(R.string.workoutPauseDuration), UnitUtils.getHourMinuteSecondTime(recorder.getPauseDuration()));
|
||||||
timeView.setText(UnitUtils.getHourMinuteSecondTime(duration));
|
|
||||||
infoViews[0].setText(distanceCaption, distance);
|
|
||||||
infoViews[1].setText(getString(R.string.workoutBurnedEnergy), recorder.getCalories() + " kcal");
|
|
||||||
infoViews[2].setText(getString(R.string.workoutAvgSpeedShort), avgSpeed);
|
|
||||||
infoViews[3].setText(getString(R.string.workoutPauseDuration), UnitUtils.getHourMinuteSecondTime(recorder.getPauseDuration()));
|
|
||||||
}
|
|
||||||
|
|
||||||
voiceAnnouncements.check(recorder);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stop(){
|
private void stop(){
|
||||||
@ -251,14 +235,14 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
|||||||
.create().show();
|
.create().show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkPermissions() {
|
void checkPermissions(){
|
||||||
if (!hasPermission()) {
|
if (!hasPermission()) {
|
||||||
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 10);
|
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 10);
|
||||||
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 10);
|
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasPermission() {
|
public boolean hasPermission(){
|
||||||
return ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|
return ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|
||||||
|| ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;
|
|| ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;
|
||||||
}
|
}
|
||||||
@ -269,12 +253,12 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stopListener() {
|
public void stopListener(){
|
||||||
stopService(locationListener);
|
stopService(locationListener);
|
||||||
stopService(pressureService);
|
stopService(pressureService);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startListener() {
|
public void startListener(){
|
||||||
if(locationListener == null){
|
if(locationListener == null){
|
||||||
locationListener= new Intent(this, LocationListener.class);
|
locationListener= new Intent(this, LocationListener.class);
|
||||||
pressureService= new Intent(this, PressureService.class);
|
pressureService= new Intent(this, PressureService.class);
|
||||||
@ -320,15 +304,9 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
|||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
recorder.stop();
|
recorder.stop();
|
||||||
saveIfNotSaved(); // Important to save
|
saveIfNotSaved(); // Important
|
||||||
|
|
||||||
// Clear map
|
|
||||||
mapView.destroyAll();
|
mapView.destroyAll();
|
||||||
AndroidGraphicFactory.clearResourceMemoryCache();
|
AndroidGraphicFactory.clearResourceMemoryCache();
|
||||||
|
|
||||||
// Shutdown TTS
|
|
||||||
voiceAnnouncements.destroy();
|
|
||||||
|
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
if(wakeLock.isHeld()){
|
if(wakeLock.isHeld()){
|
||||||
wakeLock.release();
|
wakeLock.release();
|
||||||
@ -346,18 +324,10 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
|||||||
|
|
||||||
public void onResume(){
|
public void onResume(){
|
||||||
super.onResume();
|
super.onResume();
|
||||||
enableLockScreenVisibility();
|
|
||||||
downloadLayer.onResume();
|
downloadLayer.onResume();
|
||||||
isResumed= true;
|
isResumed= true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enableLockScreenVisibility() {
|
|
||||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
|
|
||||||
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
|
|
||||||
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
|
|
||||||
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
|
|
||||||
}
|
|
||||||
|
|
||||||
@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.
|
||||||
@ -392,27 +362,13 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
|||||||
gpsFound= true;
|
gpsFound= true;
|
||||||
hideWaitOverlay();
|
hideWaitOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
AnnouncementGPSStatus announcement = new AnnouncementGPSStatus(RecordWorkoutActivity.this);
|
|
||||||
if (announcement.isEnabled()) {
|
|
||||||
if (oldState == WorkoutRecorder.GpsState.SIGNAL_LOST) { // GPS Signal found
|
|
||||||
voiceAnnouncements.speak(announcement.getSpokenGPSFound());
|
|
||||||
} else if (state == WorkoutRecorder.GpsState.SIGNAL_LOST) {
|
|
||||||
voiceAnnouncements.speak(announcement.getSpokenGPSLost());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static class InfoViewHolder{
|
||||||
public void onVoiceAnnouncementIsReady(boolean available) {
|
TextView titleView, valueView;
|
||||||
}
|
|
||||||
|
|
||||||
static class InfoViewHolder {
|
public InfoViewHolder(TextView titleView, TextView valueView) {
|
||||||
final TextView titleView;
|
|
||||||
final TextView valueView;
|
|
||||||
|
|
||||||
InfoViewHolder(TextView titleView, TextView valueView) {
|
|
||||||
this.titleView = titleView;
|
this.titleView = titleView;
|
||||||
this.valueView = valueView;
|
this.valueView = valueView;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -25,31 +25,127 @@ import android.app.AlertDialog;
|
|||||||
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;
|
||||||
|
import android.media.Ringtone;
|
||||||
|
import android.media.RingtoneManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.preference.ListPreference;
|
||||||
|
import android.preference.Preference;
|
||||||
|
import android.preference.PreferenceActivity;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
import android.preference.RingtonePreference;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.NumberPicker;
|
import android.widget.NumberPicker;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
import androidx.core.app.ActivityCompat;
|
import androidx.core.app.ActivityCompat;
|
||||||
import androidx.core.content.FileProvider;
|
import androidx.core.content.FileProvider;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
import de.tadris.fitness.R;
|
import de.tadris.fitness.R;
|
||||||
import de.tadris.fitness.export.BackupController;
|
import de.tadris.fitness.util.export.Exporter;
|
||||||
import de.tadris.fitness.export.RestoreController;
|
|
||||||
import de.tadris.fitness.recording.announcement.VoiceAnnouncements;
|
|
||||||
import de.tadris.fitness.util.FileUtils;
|
|
||||||
import de.tadris.fitness.util.unit.UnitUtils;
|
import de.tadris.fitness.util.unit.UnitUtils;
|
||||||
import de.tadris.fitness.view.ProgressDialogController;
|
import de.tadris.fitness.view.ProgressDialogController;
|
||||||
|
|
||||||
public class SettingsActivity extends FitoTrackSettingsActivity {
|
public class SettingsActivity extends PreferenceActivity {
|
||||||
|
|
||||||
private final Handler mHandler = new Handler();
|
|
||||||
|
protected void shareFile(Uri uri){
|
||||||
|
Intent intentShareFile = new Intent(Intent.ACTION_SEND);
|
||||||
|
intentShareFile.setDataAndType(uri, getContentResolver().getType(uri));
|
||||||
|
intentShareFile.putExtra(Intent.EXTRA_STREAM, uri);
|
||||||
|
intentShareFile.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
|
||||||
|
startActivity(Intent.createChooser(intentShareFile, getString(R.string.shareFile)));
|
||||||
|
|
||||||
|
Log.d("Export", uri.toString());
|
||||||
|
Log.d("Export", getContentResolver().getType(uri));
|
||||||
|
try {
|
||||||
|
Log.d("Export", new BufferedInputStream(getContentResolver().openInputStream(uri)).toString());
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void showErrorDialog(Exception e, @StringRes int title, @StringRes int message){
|
||||||
|
new AlertDialog.Builder(this)
|
||||||
|
.setTitle(title)
|
||||||
|
.setMessage(getString(message) + "\n\n" + e.getMessage())
|
||||||
|
.setPositiveButton(R.string.okay, null)
|
||||||
|
.create().show();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A preference value change listener that updates the preference's summary
|
||||||
|
* to reflect its new value.
|
||||||
|
*/
|
||||||
|
private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = (preference, value) -> {
|
||||||
|
String stringValue = value.toString();
|
||||||
|
|
||||||
|
if (preference instanceof ListPreference) {
|
||||||
|
// For list preferences, look up the correct display value in
|
||||||
|
// the preference's 'entries' list.
|
||||||
|
ListPreference listPreference = (ListPreference) preference;
|
||||||
|
int index = listPreference.findIndexOfValue(stringValue);
|
||||||
|
|
||||||
|
// Set the summary to reflect the new value.
|
||||||
|
preference.setSummary(
|
||||||
|
index >= 0
|
||||||
|
? listPreference.getEntries()[index]
|
||||||
|
: null);
|
||||||
|
|
||||||
|
} else if (preference instanceof RingtonePreference) {
|
||||||
|
// For ringtone preferences, look up the correct display value
|
||||||
|
// using RingtoneManager.
|
||||||
|
if (TextUtils.isEmpty(stringValue)) {
|
||||||
|
// Empty values correspond to 'silent' (no ringtone).
|
||||||
|
preference.setSummary(R.string.pref_ringtone_silent);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Ringtone ringtone = RingtoneManager.getRingtone(
|
||||||
|
preference.getContext(), Uri.parse(stringValue));
|
||||||
|
|
||||||
|
if (ringtone == null) {
|
||||||
|
// Clear the summary if there was a lookup error.
|
||||||
|
preference.setSummary(null);
|
||||||
|
} else {
|
||||||
|
// Set the summary to reflect the new ringtone display
|
||||||
|
// name.
|
||||||
|
String name = ringtone.getTitle(preference.getContext());
|
||||||
|
preference.setSummary(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// For all other preferences, set the summary to the value's
|
||||||
|
// simple string representation.
|
||||||
|
preference.setSummary(stringValue);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
private static void bindPreferenceSummaryToValue(Preference preference) {
|
||||||
|
// Set the listener to watch for value changes.
|
||||||
|
preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
|
||||||
|
|
||||||
|
// Trigger the listener immediately with the preference's
|
||||||
|
// current value.
|
||||||
|
sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
|
||||||
|
PreferenceManager
|
||||||
|
.getDefaultSharedPreferences(preference.getContext())
|
||||||
|
.getString(preference.getKey(), ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Handler mHandler= new Handler();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@ -62,62 +158,22 @@ public class SettingsActivity extends FitoTrackSettingsActivity {
|
|||||||
|
|
||||||
bindPreferenceSummaryToValue(findPreference("unitSystem"));
|
bindPreferenceSummaryToValue(findPreference("unitSystem"));
|
||||||
bindPreferenceSummaryToValue(findPreference("mapStyle"));
|
bindPreferenceSummaryToValue(findPreference("mapStyle"));
|
||||||
bindPreferenceSummaryToValue(findPreference("themeSetting"));
|
|
||||||
findPreference("themeSetting").setOnPreferenceChangeListener((preference, newValue) -> {
|
|
||||||
sBindPreferenceSummaryToValueListener.onPreferenceChange(preference, newValue);
|
|
||||||
Toast.makeText(SettingsActivity.this, R.string.hintRestart, Toast.LENGTH_LONG).show();
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
findPreference("weight").setOnPreferenceClickListener(preference -> {
|
findPreference("weight").setOnPreferenceClickListener(preference -> showWeightPicker());
|
||||||
showWeightPicker();
|
findPreference("import").setOnPreferenceClickListener(preference -> showImportDialog());
|
||||||
return true;
|
findPreference("export").setOnPreferenceClickListener(preference -> showExportDialog());
|
||||||
});
|
|
||||||
findPreference("speech").setOnPreferenceClickListener(preference -> {
|
|
||||||
checkTTSandShowConfig();
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
findPreference("import").setOnPreferenceClickListener(preference -> {
|
|
||||||
showImportDialog();
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
findPreference("export").setOnPreferenceClickListener(preference -> {
|
|
||||||
showExportDialog();
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private VoiceAnnouncements voiceAnnouncements;
|
private boolean showExportDialog(){
|
||||||
|
|
||||||
private void checkTTSandShowConfig() {
|
|
||||||
voiceAnnouncements = new VoiceAnnouncements(this, available -> {
|
|
||||||
if (available) {
|
|
||||||
showSpeechConfig();
|
|
||||||
} else {
|
|
||||||
// TextToSpeech is not available
|
|
||||||
Toast.makeText(SettingsActivity.this, R.string.ttsNotAvailable, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
if (voiceAnnouncements != null) {
|
|
||||||
voiceAnnouncements.destroy();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showSpeechConfig() {
|
|
||||||
startActivity(new Intent(this, VoiceAnnouncementsSettingsActivity.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showExportDialog() {
|
|
||||||
if (!hasPermission()) {
|
|
||||||
requestPermissions();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
new AlertDialog.Builder(this)
|
new AlertDialog.Builder(this)
|
||||||
.setTitle(R.string.exportData)
|
.setTitle(R.string.exportData)
|
||||||
.setMessage(R.string.exportDataSummary)
|
.setMessage(R.string.exportDataSummary)
|
||||||
.setNegativeButton(R.string.cancel, null)
|
.setNegativeButton(R.string.cancel, null)
|
||||||
.setPositiveButton(R.string.backup, (dialog, which) -> exportBackup()).create().show();
|
.setPositiveButton(R.string.backup, (dialog, which) -> {
|
||||||
|
exportBackup();
|
||||||
|
}).create().show();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void exportBackup(){
|
private void exportBackup(){
|
||||||
@ -126,18 +182,15 @@ public class SettingsActivity extends FitoTrackSettingsActivity {
|
|||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try{
|
try{
|
||||||
String file= getFilesDir().getAbsolutePath() + "/shared/backup.ftb";
|
String file= getFilesDir().getAbsolutePath() + "/shared/backup.ftb";
|
||||||
File parent = new File(file).getParentFile();
|
new File(file).getParentFile().mkdirs();
|
||||||
if (!parent.exists() && !parent.mkdirs()) {
|
|
||||||
throw new IOException("Cannot write");
|
|
||||||
}
|
|
||||||
Uri uri= FileProvider.getUriForFile(getBaseContext(), "de.tadris.fitness.fileprovider", new File(file));
|
Uri uri= FileProvider.getUriForFile(getBaseContext(), "de.tadris.fitness.fileprovider", new File(file));
|
||||||
|
|
||||||
BackupController backupController= new BackupController(getBaseContext(), new File(file), (progress, action) -> mHandler.post(() -> dialogController.setProgress(progress, action)));
|
Exporter.exportData(getBaseContext(), new File(file),
|
||||||
backupController.exportData();
|
(progress, action) -> mHandler.post(() -> dialogController.setProgress(progress, action)));
|
||||||
|
|
||||||
mHandler.post(() -> {
|
mHandler.post(() -> {
|
||||||
dialogController.cancel();
|
dialogController.cancel();
|
||||||
FileUtils.saveOrShareFile(this, uri, "ftb");
|
shareFile(uri);
|
||||||
});
|
});
|
||||||
}catch (Exception e){
|
}catch (Exception e){
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@ -149,25 +202,28 @@ public class SettingsActivity extends FitoTrackSettingsActivity {
|
|||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showImportDialog() {
|
private boolean showImportDialog(){
|
||||||
if(!hasPermission()){
|
if(!hasPermission()){
|
||||||
requestPermissions();
|
requestPermissions();
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
new AlertDialog.Builder(this)
|
new AlertDialog.Builder(this)
|
||||||
.setTitle(R.string.importBackup)
|
.setTitle(R.string.importBackup)
|
||||||
.setMessage(R.string.importBackupMessage)
|
.setMessage(R.string.importBackupMessage)
|
||||||
.setNegativeButton(R.string.cancel, null)
|
.setNegativeButton(R.string.cancel, null)
|
||||||
.setPositiveButton(R.string.restore, (dialog, which) -> importBackup()).create().show();
|
.setPositiveButton(R.string.restore, (dialog, which) -> {
|
||||||
|
importBackup();
|
||||||
|
}).create().show();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requestPermissions() {
|
void requestPermissions(){
|
||||||
if (!hasPermission()) {
|
if (!hasPermission()) {
|
||||||
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 10);
|
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasPermission() {
|
public boolean hasPermission(){
|
||||||
return ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
|
return ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,10 +239,12 @@ public class SettingsActivity extends FitoTrackSettingsActivity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
if (requestCode == FILE_SELECT_CODE) {
|
switch (requestCode) {
|
||||||
if (resultCode == RESULT_OK) {
|
case FILE_SELECT_CODE:
|
||||||
importBackup(data.getData());
|
if (resultCode == RESULT_OK){
|
||||||
}
|
importBackup(data.getData());
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
}
|
}
|
||||||
@ -196,10 +254,10 @@ public class SettingsActivity extends FitoTrackSettingsActivity {
|
|||||||
dialogController.show();
|
dialogController.show();
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try{
|
try{
|
||||||
RestoreController restoreController= new RestoreController(getBaseContext(), uri,
|
Exporter.importData(getBaseContext(), uri,
|
||||||
(progress, action) -> mHandler.post(() -> dialogController.setProgress(progress, action)));
|
(progress, action) -> mHandler.post(() -> dialogController.setProgress(progress, action)));
|
||||||
restoreController.restoreData();
|
|
||||||
|
|
||||||
|
// DO on backup finished
|
||||||
mHandler.post(dialogController::cancel);
|
mHandler.post(dialogController::cancel);
|
||||||
}catch (Exception e){
|
}catch (Exception e){
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@ -211,7 +269,7 @@ public class SettingsActivity extends FitoTrackSettingsActivity {
|
|||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showWeightPicker() {
|
private boolean showWeightPicker() {
|
||||||
UnitUtils.setUnit(this); // Maybe the user changed unit system
|
UnitUtils.setUnit(this); // Maybe the user changed unit system
|
||||||
|
|
||||||
final AlertDialog.Builder d = new AlertDialog.Builder(this);
|
final AlertDialog.Builder d = new AlertDialog.Builder(this);
|
||||||
@ -222,8 +280,7 @@ public class SettingsActivity extends FitoTrackSettingsActivity {
|
|||||||
np.setMaxValue((int) UnitUtils.CHOSEN_SYSTEM.getWeightFromKilogram(150));
|
np.setMaxValue((int) UnitUtils.CHOSEN_SYSTEM.getWeightFromKilogram(150));
|
||||||
np.setMinValue((int) UnitUtils.CHOSEN_SYSTEM.getWeightFromKilogram(20));
|
np.setMinValue((int) UnitUtils.CHOSEN_SYSTEM.getWeightFromKilogram(20));
|
||||||
np.setFormatter(value -> value + " " + UnitUtils.CHOSEN_SYSTEM.getWeightUnit());
|
np.setFormatter(value -> value + " " + UnitUtils.CHOSEN_SYSTEM.getWeightUnit());
|
||||||
final String preferenceVariable = "weight";
|
np.setValue((int)Math.round(UnitUtils.CHOSEN_SYSTEM.getWeightFromKilogram(preferences.getInt("weight", 80))));
|
||||||
np.setValue((int)Math.round(UnitUtils.CHOSEN_SYSTEM.getWeightFromKilogram(preferences.getInt(preferenceVariable, 80))));
|
|
||||||
np.setWrapSelectorWheel(false);
|
np.setWrapSelectorWheel(false);
|
||||||
|
|
||||||
d.setView(v);
|
d.setView(v);
|
||||||
@ -232,10 +289,12 @@ public class SettingsActivity extends FitoTrackSettingsActivity {
|
|||||||
d.setPositiveButton(R.string.okay, (dialog, which) -> {
|
d.setPositiveButton(R.string.okay, (dialog, which) -> {
|
||||||
int unitValue= np.getValue();
|
int unitValue= np.getValue();
|
||||||
int kilograms= (int)Math.round(UnitUtils.CHOSEN_SYSTEM.getKilogramFromUnit(unitValue));
|
int kilograms= (int)Math.round(UnitUtils.CHOSEN_SYSTEM.getKilogramFromUnit(unitValue));
|
||||||
preferences.edit().putInt(preferenceVariable, kilograms).apply();
|
preferences.edit().putInt("weight", kilograms).apply();
|
||||||
});
|
});
|
||||||
|
|
||||||
d.create().show();
|
d.create().show();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -249,4 +308,24 @@ public class SettingsActivity extends FitoTrackSettingsActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
UnitUtils.setUnit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemSelected(int featureId, MenuItem item) {
|
||||||
|
int id = item.getItemId();
|
||||||
|
if (id == android.R.id.home) {
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onMenuItemSelected(featureId, item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -20,20 +20,25 @@
|
|||||||
package de.tadris.fitness.activity;
|
package de.tadris.fitness.activity;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.graphics.Typeface;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
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.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.Spinner;
|
import android.widget.Spinner;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.core.content.FileProvider;
|
import androidx.core.content.FileProvider;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@ -45,7 +50,6 @@ import de.tadris.fitness.data.WorkoutSample;
|
|||||||
import de.tadris.fitness.osm.OAuthAuthentication;
|
import de.tadris.fitness.osm.OAuthAuthentication;
|
||||||
import de.tadris.fitness.osm.OsmTraceUploader;
|
import de.tadris.fitness.osm.OsmTraceUploader;
|
||||||
import de.tadris.fitness.util.DialogUtils;
|
import de.tadris.fitness.util.DialogUtils;
|
||||||
import de.tadris.fitness.util.FileUtils;
|
|
||||||
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;
|
||||||
@ -54,7 +58,6 @@ import oauth.signpost.OAuthConsumer;
|
|||||||
|
|
||||||
public class ShowWorkoutActivity extends WorkoutActivity implements DialogUtils.WorkoutDeleter {
|
public class ShowWorkoutActivity extends WorkoutActivity implements DialogUtils.WorkoutDeleter {
|
||||||
|
|
||||||
TextView commentView;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@ -63,13 +66,14 @@ public class ShowWorkoutActivity extends WorkoutActivity implements DialogUtils.
|
|||||||
initBeforeContent();
|
initBeforeContent();
|
||||||
|
|
||||||
setContentView(R.layout.activity_show_workout);
|
setContentView(R.layout.activity_show_workout);
|
||||||
initRoot();
|
root= findViewById(R.id.showWorkoutRoot);
|
||||||
|
|
||||||
initAfterContent();
|
initAfterContent();
|
||||||
|
|
||||||
commentView = addText("", true);
|
addText(getString(R.string.comment) + ": " + workout.comment).setOnClickListener(v -> {
|
||||||
commentView.setOnClickListener(v -> openEditCommentDialog());
|
TextView textView= (TextView)v;
|
||||||
updateCommentText();
|
openEditCommentDialog(textView);
|
||||||
|
});
|
||||||
|
|
||||||
addTitle(getString(R.string.workoutTime));
|
addTitle(getString(R.string.workoutTime));
|
||||||
addKeyValue(getString(R.string.workoutDate), getDate());
|
addKeyValue(getString(R.string.workoutDate), getDate());
|
||||||
@ -80,91 +84,110 @@ public class ShowWorkoutActivity extends WorkoutActivity implements DialogUtils.
|
|||||||
|
|
||||||
addKeyValue(getString(R.string.workoutDistance), UnitUtils.getDistance(workout.length), getString(R.string.workoutPace), UnitUtils.getPace(workout.avgPace));
|
addKeyValue(getString(R.string.workoutDistance), UnitUtils.getDistance(workout.length), getString(R.string.workoutPace), UnitUtils.getPace(workout.avgPace));
|
||||||
|
|
||||||
if (hasSamples()) {
|
addTitle(getString(R.string.workoutRoute));
|
||||||
addTitle(getString(R.string.workoutRoute));
|
|
||||||
|
|
||||||
addMap();
|
addMap();
|
||||||
|
|
||||||
map.setClickable(false);
|
|
||||||
mapRoot.setOnClickListener(v -> startActivity(new Intent(ShowWorkoutActivity.this, ShowWorkoutMapActivity.class)));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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.workoutAvgSpeedShort), UnitUtils.getSpeed(workout.avgSpeed),
|
addKeyValue(getString(R.string.workoutAvgSpeed), UnitUtils.getSpeed(workout.avgSpeed),
|
||||||
getString(R.string.workoutTopSpeed), UnitUtils.getSpeed(workout.topSpeed));
|
getString(R.string.workoutTopSpeed), UnitUtils.getSpeed(workout.topSpeed));
|
||||||
|
|
||||||
if (hasSamples()) {
|
addSpeedDiagram();
|
||||||
|
|
||||||
addSpeedDiagram();
|
speedDiagram.setOnClickListener(v -> startDiagramActivity(ShowWorkoutMapDiagramActivity.DIAGRAM_TYPE_SPEED));
|
||||||
|
|
||||||
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)));
|
||||||
|
|
||||||
if (hasSamples()) {
|
addTitle(getString(R.string.height));
|
||||||
addTitle(getString(R.string.height));
|
|
||||||
|
|
||||||
addKeyValue(getString(R.string.workoutAscent), UnitUtils.getDistance((int) workout.ascent),
|
addKeyValue(getString(R.string.workoutAscent), UnitUtils.getDistance((int)workout.ascent),
|
||||||
getString(R.string.workoutDescent), UnitUtils.getDistance((int) workout.descent));
|
getString(R.string.workoutDescent), UnitUtils.getDistance((int)workout.descent));
|
||||||
|
|
||||||
addHeightDiagram();
|
addHeightDiagram();
|
||||||
|
|
||||||
heightDiagram.setOnClickListener(v -> startDiagramActivity(ShowWorkoutMapDiagramActivity.DIAGRAM_TYPE_HEIGHT));
|
heightDiagram.setOnClickListener(v -> startDiagramActivity(ShowWorkoutMapDiagramActivity.DIAGRAM_TYPE_HEIGHT));
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startDiagramActivity(String diagramType) {
|
void startDiagramActivity(String diagramType){
|
||||||
ShowWorkoutMapDiagramActivity.DIAGRAM_TYPE= diagramType;
|
ShowWorkoutMapDiagramActivity.DIAGRAM_TYPE= diagramType;
|
||||||
startActivity(new Intent(ShowWorkoutActivity.this, ShowWorkoutMapDiagramActivity.class));
|
startActivity(new Intent(ShowWorkoutActivity.this, ShowWorkoutMapDiagramActivity.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void openEditCommentDialog() {
|
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);
|
||||||
editText.setSingleLine(true);
|
editText.setSingleLine(true);
|
||||||
new AlertDialog.Builder(this)
|
new AlertDialog.Builder(this)
|
||||||
.setTitle(R.string.enterComment)
|
.setTitle(R.string.enterComment)
|
||||||
.setPositiveButton(R.string.okay, (dialog, which) -> changeComment(editText.getText().toString()))
|
.setPositiveButton(R.string.okay, (dialog, which) -> changeComment(editText.getText().toString(), change))
|
||||||
.setView(editText).create().show();
|
.setView(editText).create().show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void changeComment(String comment) {
|
void changeComment(String comment, TextView onChange){
|
||||||
workout.comment= comment;
|
workout.comment= comment;
|
||||||
Instance.getInstance(this).db.workoutDao().updateWorkout(workout);
|
Instance.getInstance(this).db.workoutDao().updateWorkout(workout);
|
||||||
updateCommentText();
|
onChange.setText(getString(R.string.comment) + ": " + workout.comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateCommentText() {
|
String getDate(){
|
||||||
String str = "";
|
|
||||||
if (workout.edited) {
|
|
||||||
str += getString(R.string.workoutEdited);
|
|
||||||
}
|
|
||||||
if (workout.comment != null && workout.comment.length() > 0) {
|
|
||||||
if (str.length() > 0) {
|
|
||||||
str += "\n";
|
|
||||||
}
|
|
||||||
str += getString(R.string.comment) + ": " + workout.comment;
|
|
||||||
}
|
|
||||||
if (str.length() == 0) {
|
|
||||||
str = getString(R.string.noComment);
|
|
||||||
}
|
|
||||||
commentView.setText(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getDate() {
|
|
||||||
return SimpleDateFormat.getDateInstance().format(new Date(workout.start));
|
return SimpleDateFormat.getDateInstance().format(new Date(workout.start));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void addTitle(String title){
|
||||||
|
TextView textView= new TextView(this);
|
||||||
|
textView.setText(title);
|
||||||
|
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
|
||||||
|
textView.setTextColor(getThemePrimaryColor());
|
||||||
|
textView.setTypeface(Typeface.DEFAULT_BOLD);
|
||||||
|
textView.setAllCaps(true);
|
||||||
|
textView.setPadding(0, 20, 0, 0);
|
||||||
|
|
||||||
|
root.addView(textView);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextView addText(String text){
|
||||||
|
TextView textView= new TextView(this);
|
||||||
|
textView.setText(text);
|
||||||
|
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
|
||||||
|
textView.setTextColor(getThemePrimaryColor());
|
||||||
|
textView.setPadding(0, 20, 0, 0);
|
||||||
|
|
||||||
|
root.addView(textView);
|
||||||
|
|
||||||
|
return textView;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addKeyValue(String key1, String value1){
|
||||||
|
addKeyValue(key1, value1, "", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void addKeyValue(String key1, String value1, String key2, String value2){
|
||||||
|
View v= getLayoutInflater().inflate(R.layout.show_entry, root, false);
|
||||||
|
|
||||||
|
TextView title1= v.findViewById(R.id.v1title);
|
||||||
|
TextView title2= v.findViewById(R.id.v2title);
|
||||||
|
TextView v1= v.findViewById(R.id.v1value);
|
||||||
|
TextView v2= v.findViewById(R.id.v2value);
|
||||||
|
|
||||||
|
title1.setText(key1);
|
||||||
|
title2.setText(key2);
|
||||||
|
v1.setText(value1);
|
||||||
|
v2.setText(value2);
|
||||||
|
|
||||||
|
root.addView(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@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.
|
||||||
@ -182,27 +205,19 @@ public class ShowWorkoutActivity extends WorkoutActivity implements DialogUtils.
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void exportToGpx(){
|
private void exportToGpx(){
|
||||||
if (!hasStoragePermission()) {
|
|
||||||
requestStoragePermissions();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ProgressDialogController dialogController= new ProgressDialogController(this, getString(R.string.exporting));
|
ProgressDialogController dialogController= new ProgressDialogController(this, getString(R.string.exporting));
|
||||||
dialogController.setIndeterminate(true);
|
dialogController.setIndeterminate(true);
|
||||||
dialogController.show();
|
dialogController.show();
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try{
|
try{
|
||||||
String file= getFilesDir().getAbsolutePath() + "/shared/workout.gpx";
|
String file= getFilesDir().getAbsolutePath() + "/shared/workout.gpx";
|
||||||
File parent = new File(file).getParentFile();
|
new File(file).getParentFile().mkdirs();
|
||||||
if (!parent.exists() && !parent.mkdirs()) {
|
|
||||||
throw new IOException("Cannot write to " + file);
|
|
||||||
}
|
|
||||||
Uri uri= FileProvider.getUriForFile(getBaseContext(), "de.tadris.fitness.fileprovider", new File(file));
|
Uri uri= FileProvider.getUriForFile(getBaseContext(), "de.tadris.fitness.fileprovider", new File(file));
|
||||||
|
|
||||||
GpxExporter.exportWorkout(getBaseContext(), workout, new File(file));
|
GpxExporter.exportWorkout(getBaseContext(), workout, new File(file));
|
||||||
dialogController.cancel();
|
dialogController.cancel();
|
||||||
mHandler.post(() -> FileUtils.saveOrShareFile(this, uri, "gpx"));
|
mHandler.post(() -> shareFile(uri));
|
||||||
}catch (Exception e){
|
}catch (Exception e){
|
||||||
e.printStackTrace();
|
|
||||||
mHandler.post(() -> showErrorDialog(e, R.string.error, R.string.errorGpxExportFailed));
|
mHandler.post(() -> showErrorDialog(e, R.string.error, R.string.errorGpxExportFailed));
|
||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
@ -230,7 +245,7 @@ public class ShowWorkoutActivity extends WorkoutActivity implements DialogUtils.
|
|||||||
authentication.authenticateIfNecessary();
|
authentication.authenticateIfNecessary();
|
||||||
}
|
}
|
||||||
|
|
||||||
private AlertDialog dialog = null;
|
AlertDialog dialog= null;
|
||||||
private void showUploadOptions(){
|
private void showUploadOptions(){
|
||||||
dialog= new AlertDialog.Builder(this)
|
dialog= new AlertDialog.Builder(this)
|
||||||
.setTitle(R.string.actionUploadToOSM)
|
.setTitle(R.string.actionUploadToOSM)
|
||||||
@ -271,15 +286,8 @@ public class ShowWorkoutActivity extends WorkoutActivity implements DialogUtils.
|
|||||||
case R.id.actionUploadOSM:
|
case R.id.actionUploadOSM:
|
||||||
prepareUpload();
|
prepareUpload();
|
||||||
return true;
|
return true;
|
||||||
case R.id.actionEditComment:
|
|
||||||
openEditCommentDialog();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
void initRoot() {
|
|
||||||
root = findViewById(R.id.showWorkoutRoot);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -32,7 +32,7 @@ public class ShowWorkoutMapActivity extends WorkoutActivity {
|
|||||||
initBeforeContent();
|
initBeforeContent();
|
||||||
|
|
||||||
setContentView(R.layout.activity_show_workout_map);
|
setContentView(R.layout.activity_show_workout_map);
|
||||||
initRoot();
|
root= findViewById(R.id.showWorkoutMapParent);
|
||||||
|
|
||||||
initAfterContent();
|
initAfterContent();
|
||||||
|
|
||||||
@ -43,8 +43,4 @@ public class ShowWorkoutMapActivity extends WorkoutActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void initRoot() {
|
|
||||||
root = findViewById(R.id.showWorkoutMapParent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -36,7 +36,7 @@ public class ShowWorkoutMapDiagramActivity extends WorkoutActivity {
|
|||||||
initBeforeContent();
|
initBeforeContent();
|
||||||
|
|
||||||
setContentView(R.layout.activity_show_workout_map_diagram);
|
setContentView(R.layout.activity_show_workout_map_diagram);
|
||||||
initRoot();
|
root= findViewById(R.id.showWorkoutMapParent);
|
||||||
|
|
||||||
initAfterContent();
|
initAfterContent();
|
||||||
|
|
||||||
@ -54,8 +54,4 @@ public class ShowWorkoutMapDiagramActivity extends WorkoutActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void initRoot() {
|
|
||||||
root = findViewById(R.id.showWorkoutMapParent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -1,102 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
*
|
|
||||||
* This file is part of FitoTrack
|
|
||||||
*
|
|
||||||
* FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.tadris.fitness.activity;
|
|
||||||
|
|
||||||
import android.app.ActionBar;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.NumberPicker;
|
|
||||||
|
|
||||||
import de.tadris.fitness.R;
|
|
||||||
import de.tadris.fitness.util.unit.UnitUtils;
|
|
||||||
|
|
||||||
public class VoiceAnnouncementsSettingsActivity extends FitoTrackSettingsActivity {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setupActionBar();
|
|
||||||
|
|
||||||
setTitle(R.string.voiceAnnouncementsTitle);
|
|
||||||
|
|
||||||
addPreferencesFromResource(R.xml.preferences_voice_announcements);
|
|
||||||
|
|
||||||
bindPreferenceSummaryToValue(findPreference("announcementMode"));
|
|
||||||
|
|
||||||
findPreference("speechConfig").setOnPreferenceClickListener(preference -> {
|
|
||||||
showSpeechConfig();
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showSpeechConfig() {
|
|
||||||
UnitUtils.setUnit(this); // Maybe the user changed unit system
|
|
||||||
|
|
||||||
final AlertDialog.Builder d = new AlertDialog.Builder(this);
|
|
||||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
|
||||||
d.setTitle(getString(R.string.pref_voice_announcements_summary));
|
|
||||||
View v = getLayoutInflater().inflate(R.layout.dialog_spoken_updates_picker, null);
|
|
||||||
|
|
||||||
NumberPicker npT = v.findViewById(R.id.spokenUpdatesTimePicker);
|
|
||||||
npT.setMaxValue(60);
|
|
||||||
npT.setMinValue(0);
|
|
||||||
npT.setFormatter(value -> value == 0 ? "No speech" : value + " min");
|
|
||||||
final String updateTimeVariable = "spokenUpdateTimePeriod";
|
|
||||||
npT.setValue(preferences.getInt(updateTimeVariable, 0));
|
|
||||||
npT.setWrapSelectorWheel(false);
|
|
||||||
|
|
||||||
final String distanceUnit = " " + UnitUtils.CHOSEN_SYSTEM.getLongDistanceUnit();
|
|
||||||
NumberPicker npD = v.findViewById(R.id.spokenUpdatesDistancePicker);
|
|
||||||
npD.setMaxValue(10);
|
|
||||||
npD.setMinValue(0);
|
|
||||||
npD.setFormatter(value -> value == 0 ? "No speech" : value + distanceUnit);
|
|
||||||
final String updateDistanceVariable = "spokenUpdateDistancePeriod";
|
|
||||||
npD.setValue(preferences.getInt(updateDistanceVariable, 0));
|
|
||||||
npD.setWrapSelectorWheel(false);
|
|
||||||
|
|
||||||
d.setView(v);
|
|
||||||
|
|
||||||
d.setNegativeButton(R.string.cancel, null);
|
|
||||||
d.setPositiveButton(R.string.okay, (dialog, which) ->
|
|
||||||
preferences.edit()
|
|
||||||
.putInt(updateTimeVariable, npT.getValue())
|
|
||||||
.putInt(updateDistanceVariable, npD.getValue())
|
|
||||||
.apply());
|
|
||||||
|
|
||||||
d.create().show();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set up the {@link android.app.ActionBar}, if the API is available.
|
|
||||||
*/
|
|
||||||
private void setupActionBar() {
|
|
||||||
ActionBar actionBar = getActionBar();
|
|
||||||
if (actionBar != null) {
|
|
||||||
// Show the Up button in the action bar.
|
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -55,45 +55,46 @@ import de.tadris.fitness.data.WorkoutManager;
|
|||||||
import de.tadris.fitness.data.WorkoutSample;
|
import de.tadris.fitness.data.WorkoutSample;
|
||||||
import de.tadris.fitness.map.MapManager;
|
import de.tadris.fitness.map.MapManager;
|
||||||
import de.tadris.fitness.map.WorkoutLayer;
|
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;
|
import de.tadris.fitness.util.unit.UnitUtils;
|
||||||
|
|
||||||
public abstract class WorkoutActivity extends InformationActivity {
|
public abstract class WorkoutActivity extends FitoTrackActivity {
|
||||||
|
|
||||||
public static Workout selectedWorkout;
|
public static Workout selectedWorkout;
|
||||||
|
|
||||||
List<WorkoutSample> samples;
|
protected List<WorkoutSample> samples;
|
||||||
Workout workout;
|
protected Workout workout;
|
||||||
private Resources.Theme theme;
|
protected ViewGroup root;
|
||||||
MapView map;
|
protected Resources.Theme theme;
|
||||||
private TileDownloadLayer downloadLayer;
|
protected MapView map;
|
||||||
private FixedPixelCircle highlightingCircle;
|
protected TileDownloadLayer downloadLayer;
|
||||||
final Handler mHandler = new Handler();
|
protected FixedPixelCircle highlightingCircle;
|
||||||
|
protected Handler mHandler= new Handler();
|
||||||
|
|
||||||
LineChart speedDiagram;
|
protected LineChart speedDiagram, heightDiagram;
|
||||||
LineChart heightDiagram;
|
|
||||||
|
|
||||||
void initBeforeContent() {
|
protected void initBeforeContent(){
|
||||||
workout= selectedWorkout;
|
workout= selectedWorkout;
|
||||||
samples= Arrays.asList(Instance.getInstance(this).db.workoutDao().getAllSamplesOfWorkout(workout.id));
|
samples= Arrays.asList(Instance.getInstance(this).db.workoutDao().getAllSamplesOfWorkout(workout.id));
|
||||||
setTheme(Instance.getInstance(this).themes.getWorkoutTypeTheme(workout.getWorkoutType()));
|
setTheme(ThemeManager.getThemeByWorkout(workout));
|
||||||
}
|
}
|
||||||
|
|
||||||
void initAfterContent() {
|
protected void initAfterContent(){
|
||||||
if (getActionBar() != null) {
|
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
setTitle(WorkoutTypeCalculator.getType(workout));
|
||||||
}
|
|
||||||
setTitle(workout.getWorkoutType().title);
|
|
||||||
|
|
||||||
theme= getTheme();
|
theme= getTheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addDiagram(SampleConverter converter) {
|
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));
|
root.addView(getDiagram(converter), new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, fullScreenItems ? ViewGroup.LayoutParams.MATCH_PARENT : getWindowManager().getDefaultDisplay().getWidth()*3/4));
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean diagramsInteractive = false;
|
protected boolean diagramsInteractive= false;
|
||||||
|
|
||||||
private LineChart getDiagram(SampleConverter converter) {
|
LineChart getDiagram(SampleConverter converter){
|
||||||
LineChart chart= new LineChart(this);
|
LineChart chart= new LineChart(this);
|
||||||
|
|
||||||
converter.onCreate();
|
converter.onCreate();
|
||||||
@ -111,7 +112,7 @@ public abstract class WorkoutActivity extends InformationActivity {
|
|||||||
dataSet.setValueTextColor(getThemePrimaryColor());
|
dataSet.setValueTextColor(getThemePrimaryColor());
|
||||||
dataSet.setDrawCircles(false);
|
dataSet.setDrawCircles(false);
|
||||||
dataSet.setLineWidth(4);
|
dataSet.setLineWidth(4);
|
||||||
dataSet.setMode(LineDataSet.Mode.HORIZONTAL_BEZIER);
|
dataSet.setMode(LineDataSet.Mode.CUBIC_BEZIER);
|
||||||
|
|
||||||
Description description= new Description();
|
Description description= new Description();
|
||||||
description.setText(converter.getDescription());
|
description.setText(converter.getDescription());
|
||||||
@ -144,7 +145,7 @@ public abstract class WorkoutActivity extends InformationActivity {
|
|||||||
return chart;
|
return chart;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDiagramValueSelected(LatLong latLong) {
|
protected void onDiagramValueSelected(LatLong latLong){
|
||||||
Paint p= AndroidGraphicFactory.INSTANCE.createPaint();
|
Paint p= AndroidGraphicFactory.INSTANCE.createPaint();
|
||||||
p.setColor(0xff693cff);
|
p.setColor(0xff693cff);
|
||||||
highlightingCircle= new FixedPixelCircle(latLong, 20, p, null);
|
highlightingCircle= new FixedPixelCircle(latLong, 20, p, null);
|
||||||
@ -165,7 +166,7 @@ public abstract class WorkoutActivity extends InformationActivity {
|
|||||||
void afterAdd(LineChart chart);
|
void afterAdd(LineChart chart);
|
||||||
}
|
}
|
||||||
|
|
||||||
private WorkoutSample findSample(SampleConverter converter, Entry entry) {
|
WorkoutSample findSample(SampleConverter converter, Entry entry){
|
||||||
for(WorkoutSample sample : samples){
|
for(WorkoutSample sample : samples){
|
||||||
if(converter.compare(sample, entry)){
|
if(converter.compare(sample, entry)){
|
||||||
return sample;
|
return sample;
|
||||||
@ -250,12 +251,12 @@ public abstract class WorkoutActivity extends InformationActivity {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean fullScreenItems = false;
|
protected boolean fullScreenItems = false;
|
||||||
LinearLayout mapRoot;
|
protected LinearLayout mapRoot;
|
||||||
|
|
||||||
void addMap(){
|
void addMap(){
|
||||||
map= new MapView(this);
|
map= new MapView(this);
|
||||||
downloadLayer = MapManager.setupMap(map);
|
downloadLayer= MapManager.setupMap(map, TileSources.Purpose.DEFAULT);
|
||||||
|
|
||||||
WorkoutLayer workoutLayer= new WorkoutLayer(samples, getThemePrimaryColor());
|
WorkoutLayer workoutLayer= new WorkoutLayer(samples, getThemePrimaryColor());
|
||||||
map.addLayer(workoutLayer);
|
map.addLayer(workoutLayer);
|
||||||
@ -289,19 +290,13 @@ public abstract class WorkoutActivity extends InformationActivity {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getMapHeight() {
|
int getMapHeight(){
|
||||||
return getWindowManager().getDefaultDisplay().getWidth()*3/4;
|
return getWindowManager().getDefaultDisplay().getWidth()*3/4;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean hasSamples() {
|
|
||||||
return samples.size() > 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
if (map != null) {
|
map.destroyAll();
|
||||||
map.destroyAll();
|
|
||||||
}
|
|
||||||
AndroidGraphicFactory.clearResourceMemoryCache();
|
AndroidGraphicFactory.clearResourceMemoryCache();
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
@ -309,16 +304,12 @@ public abstract class WorkoutActivity extends InformationActivity {
|
|||||||
@Override
|
@Override
|
||||||
public void onPause(){
|
public void onPause(){
|
||||||
super.onPause();
|
super.onPause();
|
||||||
if (downloadLayer != null) {
|
downloadLayer.onPause();
|
||||||
downloadLayer.onPause();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onResume(){
|
public void onResume(){
|
||||||
super.onResume();
|
super.onResume();
|
||||||
if (downloadLayer != null) {
|
downloadLayer.onResume();
|
||||||
downloadLayer.onResume();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -22,7 +22,7 @@ package de.tadris.fitness.data;
|
|||||||
import androidx.room.Database;
|
import androidx.room.Database;
|
||||||
import androidx.room.RoomDatabase;
|
import androidx.room.RoomDatabase;
|
||||||
|
|
||||||
@Database(version = 3, entities = {Workout.class, WorkoutSample.class})
|
@Database(version = 2, entities = {Workout.class, WorkoutSample.class})
|
||||||
public abstract class AppDatabase extends RoomDatabase {
|
public abstract class AppDatabase extends RoomDatabase {
|
||||||
public abstract WorkoutDao workoutDao();
|
public abstract WorkoutDao workoutDao();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -25,7 +25,7 @@ import android.preference.PreferenceManager;
|
|||||||
|
|
||||||
public class UserPreferences {
|
public class UserPreferences {
|
||||||
|
|
||||||
private final SharedPreferences preferences;
|
private SharedPreferences preferences;
|
||||||
|
|
||||||
public UserPreferences(Context context) {
|
public UserPreferences(Context context) {
|
||||||
this.preferences= PreferenceManager.getDefaultSharedPreferences(context);
|
this.preferences= PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
@ -35,14 +35,6 @@ public class UserPreferences {
|
|||||||
return preferences.getInt("weight", 80);
|
return preferences.getInt("weight", 80);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSpokenUpdateTimePeriod(){
|
|
||||||
return preferences.getInt("spokenUpdateTimePeriod", 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getSpokenUpdateDistancePeriod(){
|
|
||||||
return preferences.getInt("spokenUpdateDistancePeriod", 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMapStyle(){
|
public String getMapStyle(){
|
||||||
return preferences.getString("mapStyle", "osm.mapnik");
|
return preferences.getString("mapStyle", "osm.mapnik");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -19,13 +19,10 @@
|
|||||||
|
|
||||||
package de.tadris.fitness.data;
|
package de.tadris.fitness.data;
|
||||||
|
|
||||||
import androidx.room.ColumnInfo;
|
|
||||||
import androidx.room.Entity;
|
import androidx.room.Entity;
|
||||||
import androidx.room.PrimaryKey;
|
import androidx.room.PrimaryKey;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@ -34,6 +31,10 @@ import java.util.Date;
|
|||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class Workout{
|
public class Workout{
|
||||||
|
|
||||||
|
public static final String WORKOUT_TYPE_RUNNING= "running";
|
||||||
|
public static final String WORKOUT_TYPE_HIKING= "hiking";
|
||||||
|
public static final String WORKOUT_TYPE_CYCLING= "cycling";
|
||||||
|
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
public long id;
|
public long id;
|
||||||
|
|
||||||
@ -62,13 +63,12 @@ public class Workout{
|
|||||||
public double topSpeed;
|
public double topSpeed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Average pace of workout in min/km
|
* Average pace of workout in km/min
|
||||||
*/
|
*/
|
||||||
public double avgPace;
|
public double avgPace;
|
||||||
|
|
||||||
@ColumnInfo(name = "workoutType")
|
public String workoutType;
|
||||||
@JsonProperty(value = "workoutType")
|
|
||||||
public String workoutTypeId;
|
|
||||||
|
|
||||||
public float ascent;
|
public float ascent;
|
||||||
|
|
||||||
@ -76,8 +76,6 @@ public class Workout{
|
|||||||
|
|
||||||
public int calorie;
|
public int calorie;
|
||||||
|
|
||||||
public boolean edited;
|
|
||||||
|
|
||||||
public String toString(){
|
public String toString(){
|
||||||
if(comment.length() > 2){
|
if(comment.length() > 2){
|
||||||
return comment;
|
return comment;
|
||||||
@ -86,20 +84,9 @@ public class Workout{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
|
||||||
public String getDateString(){
|
public String getDateString(){
|
||||||
return SimpleDateFormat.getDateTimeInstance().format(new Date(start));
|
return SimpleDateFormat.getDateTimeInstance().format(new Date(start));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
|
||||||
public WorkoutType getWorkoutType() {
|
|
||||||
return WorkoutType.getTypeById(workoutTypeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonIgnore
|
|
||||||
public void setWorkoutType(WorkoutType workoutType) {
|
|
||||||
this.workoutTypeId = workoutType.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1,122 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
*
|
|
||||||
* This file is part of FitoTrack
|
|
||||||
*
|
|
||||||
* FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.tadris.fitness.data;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import java.util.Calendar;
|
|
||||||
|
|
||||||
import de.tadris.fitness.Instance;
|
|
||||||
import de.tadris.fitness.util.CalorieCalculator;
|
|
||||||
|
|
||||||
public class WorkoutBuilder {
|
|
||||||
|
|
||||||
private WorkoutType workoutType;
|
|
||||||
|
|
||||||
private Calendar start;
|
|
||||||
private long duration;
|
|
||||||
|
|
||||||
private int length;
|
|
||||||
|
|
||||||
private String comment;
|
|
||||||
|
|
||||||
public WorkoutBuilder() {
|
|
||||||
workoutType = WorkoutType.RUNNING;
|
|
||||||
start = Calendar.getInstance();
|
|
||||||
duration = 1000L * 60 * 10;
|
|
||||||
length = 500;
|
|
||||||
comment = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public Workout create(Context context) {
|
|
||||||
Workout workout = new Workout();
|
|
||||||
|
|
||||||
// Calculate values
|
|
||||||
workout.start = start.getTimeInMillis();
|
|
||||||
workout.duration = duration;
|
|
||||||
workout.end = workout.start + workout.duration;
|
|
||||||
|
|
||||||
workout.id = workout.start;
|
|
||||||
workout.setWorkoutType(workoutType);
|
|
||||||
|
|
||||||
workout.length = length;
|
|
||||||
|
|
||||||
workout.avgSpeed = (double) length / (double) (duration / 1000);
|
|
||||||
workout.topSpeed = 0;
|
|
||||||
workout.avgPace = ((double) workout.duration / 1000 / 60) / ((double) workout.length / 1000);
|
|
||||||
|
|
||||||
workout.pauseDuration = 0;
|
|
||||||
workout.ascent = 0;
|
|
||||||
workout.descent = 0;
|
|
||||||
|
|
||||||
workout.calorie = CalorieCalculator.calculateCalories(workout, Instance.getInstance(context).userPreferences.getUserWeight());
|
|
||||||
workout.comment = comment;
|
|
||||||
|
|
||||||
workout.edited = true;
|
|
||||||
|
|
||||||
return workout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Workout insertWorkout(Context context) {
|
|
||||||
Workout workout = create(context);
|
|
||||||
Instance.getInstance(context).db.workoutDao().insertWorkout(workout);
|
|
||||||
return workout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public WorkoutType getWorkoutType() {
|
|
||||||
return workoutType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setWorkoutType(WorkoutType workoutType) {
|
|
||||||
this.workoutType = workoutType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Calendar getStart() {
|
|
||||||
return start;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStart(Calendar start) {
|
|
||||||
this.start = start;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getDuration() {
|
|
||||||
return duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDuration(long duration) {
|
|
||||||
this.duration = duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getLength() {
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLength(int length) {
|
|
||||||
this.length = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getComment() {
|
|
||||||
return comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setComment(String comment) {
|
|
||||||
this.comment = comment;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -19,10 +19,118 @@
|
|||||||
|
|
||||||
package de.tadris.fitness.data;
|
package de.tadris.fitness.data;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.hardware.SensorManager;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.tadris.fitness.Instance;
|
||||||
|
import de.tadris.fitness.util.CalorieCalculator;
|
||||||
|
|
||||||
public class WorkoutManager {
|
public class WorkoutManager {
|
||||||
|
|
||||||
|
public static void insertWorkout(Context context, Workout workout, List<WorkoutSample> samples){
|
||||||
|
AppDatabase db= Instance.getInstance(context).db;
|
||||||
|
|
||||||
|
|
||||||
|
workout.id= System.currentTimeMillis();
|
||||||
|
|
||||||
|
// Delete Samples with same time
|
||||||
|
for(int i= samples.size()-2; i >= 0; i--){
|
||||||
|
WorkoutSample sample= samples.get(i);
|
||||||
|
WorkoutSample lastSample= samples.get(i+1);
|
||||||
|
if(sample.absoluteTime == lastSample.absoluteTime){
|
||||||
|
samples.remove(lastSample);
|
||||||
|
Log.i("WorkoutManager", "Removed samples at " + sample.absoluteTime + " rel: " + sample.relativeTime + "; " + lastSample.relativeTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculating values
|
||||||
|
double length= 0;
|
||||||
|
for(int i= 1; i < samples.size(); i++){
|
||||||
|
double sampleLength= samples.get(i - 1).toLatLong().sphericalDistance(samples.get(i).toLatLong());
|
||||||
|
long timeDiff= (samples.get(i).relativeTime - samples.get(i - 1).relativeTime) / 1000;
|
||||||
|
length+= sampleLength;
|
||||||
|
samples.get(i).speed= Math.abs(sampleLength / timeDiff);
|
||||||
|
}
|
||||||
|
workout.length= (int)length;
|
||||||
|
workout.avgSpeed= ((double) workout.length) / ((double) workout.duration / 1000);
|
||||||
|
workout.avgPace= ((double)workout.duration / 1000 / 60) / ((double) workout.length / 1000);
|
||||||
|
workout.calorie= CalorieCalculator.calculateCalories(workout, Instance.getInstance(context).userPreferences.getUserWeight());
|
||||||
|
|
||||||
|
// Setting workoutId in the samples
|
||||||
|
int i= 0;
|
||||||
|
double topSpeed= 0;
|
||||||
|
double elevationSum= 0; // Sum of elevation
|
||||||
|
double pressureSum= 0; // Sum of elevation
|
||||||
|
for(WorkoutSample sample : samples){
|
||||||
|
i++;
|
||||||
|
sample.id= workout.id + i;
|
||||||
|
sample.workoutId= workout.id;
|
||||||
|
elevationSum+= sample.elevation;
|
||||||
|
pressureSum+= sample.tmpPressure;
|
||||||
|
if(sample.speed > topSpeed){
|
||||||
|
topSpeed= sample.speed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
workout.topSpeed= topSpeed;
|
||||||
|
|
||||||
|
// Calculating height data
|
||||||
|
boolean pressureDataAvailable= samples.get(0).tmpPressure != -1;
|
||||||
|
double avgElevation= elevationSum / samples.size();
|
||||||
|
double avgPressure= pressureSum / samples.size();
|
||||||
|
|
||||||
|
workout.ascent = 0;
|
||||||
|
workout.descent = 0;
|
||||||
|
|
||||||
|
for(i= 0; i < samples.size(); i++){
|
||||||
|
WorkoutSample sample= samples.get(i);
|
||||||
|
|
||||||
|
if(pressureDataAvailable){
|
||||||
|
// Altitude Difference to Average Elevation in meters
|
||||||
|
float altitude_difference =
|
||||||
|
SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, sample.tmpPressure) -
|
||||||
|
SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, (float) avgPressure);
|
||||||
|
sample.elevation= avgElevation + altitude_difference;
|
||||||
|
} // Else: use already set GPS elevation in WorkoutSample.elevation
|
||||||
|
}
|
||||||
|
|
||||||
|
int range= 3;
|
||||||
|
for(i= 0; i < samples.size(); i++){
|
||||||
|
int min= Math.max(i-range, 0);
|
||||||
|
int max= Math.min(i+range, samples.size()-1);
|
||||||
|
samples.get(i).tmpElevation= getAverageElevation(samples.subList(min, max));
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i= 0; i < samples.size(); i++) {
|
||||||
|
WorkoutSample sample = samples.get(i);
|
||||||
|
sample.elevation= sample.tmpElevation;
|
||||||
|
if(i >= 1){
|
||||||
|
WorkoutSample lastSample= samples.get(i-1);
|
||||||
|
double diff= sample.elevation - lastSample.elevation;
|
||||||
|
if(diff > 0){
|
||||||
|
workout.ascent += diff;
|
||||||
|
}else{
|
||||||
|
workout.descent += Math.abs(diff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Saving workout and samples
|
||||||
|
db.workoutDao().insertWorkoutAndSamples(workout, samples.toArray(new WorkoutSample[0]));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double getAverageElevation(List<WorkoutSample> samples){
|
||||||
|
double sum= 0;
|
||||||
|
for(WorkoutSample sample : samples){
|
||||||
|
sum+= sample.elevation;
|
||||||
|
}
|
||||||
|
return sum / samples.size();
|
||||||
|
}
|
||||||
|
|
||||||
public static void roundSpeedValues(List<WorkoutSample> samples){
|
public static void roundSpeedValues(List<WorkoutSample> samples){
|
||||||
for(int i= 0; i < samples.size(); i++){
|
for(int i= 0; i < samples.size(); i++){
|
||||||
WorkoutSample sample= samples.get(i);
|
WorkoutSample sample= samples.get(i);
|
||||||
|
|||||||
@ -1,59 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
*
|
|
||||||
* This file is part of FitoTrack
|
|
||||||
*
|
|
||||||
* FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.tadris.fitness.data;
|
|
||||||
|
|
||||||
import androidx.annotation.StringRes;
|
|
||||||
import androidx.annotation.StyleRes;
|
|
||||||
|
|
||||||
import de.tadris.fitness.R;
|
|
||||||
|
|
||||||
public enum WorkoutType {
|
|
||||||
|
|
||||||
RUNNING("running", R.string.workoutTypeRunning, 7, true, R.style.Running, R.style.RunningDark),
|
|
||||||
HIKING("hiking", R.string.workoutTypeHiking, 7, true, R.style.Hiking, R.style.HikingDark),
|
|
||||||
CYCLING("cycling", R.string.workoutTypeCycling, 12, true, R.style.Bicycling, R.style.BicyclingDark),
|
|
||||||
OTHER("other", R.string.workoutTypeOther, 7, true, R.style.AppTheme, R.style.AppThemeDark);
|
|
||||||
|
|
||||||
public String id;
|
|
||||||
@StringRes
|
|
||||||
public int title;
|
|
||||||
public int minDistance; // Minimum distance between samples
|
|
||||||
public boolean hasGPS;
|
|
||||||
@StyleRes
|
|
||||||
public int lightTheme, darkTheme;
|
|
||||||
|
|
||||||
WorkoutType(String id, int title, int minDistance, boolean hasGPS, int lightTheme, int darkTheme) {
|
|
||||||
this.id = id;
|
|
||||||
this.title = title;
|
|
||||||
this.minDistance = minDistance;
|
|
||||||
this.hasGPS = hasGPS;
|
|
||||||
this.lightTheme = lightTheme;
|
|
||||||
this.darkTheme = darkTheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static WorkoutType getTypeById(String id) {
|
|
||||||
for (WorkoutType type : values()) {
|
|
||||||
if (type.id.equals(id)) {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return OTHER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
*
|
|
||||||
* This file is part of FitoTrack
|
|
||||||
*
|
|
||||||
* FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.tadris.fitness.dialog;
|
|
||||||
|
|
||||||
import android.app.DatePickerDialog;
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.app.DialogFragment;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.widget.DatePicker;
|
|
||||||
|
|
||||||
import java.util.Calendar;
|
|
||||||
|
|
||||||
public class DatePickerFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener {
|
|
||||||
|
|
||||||
public DatePickerCallback callback;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
||||||
// Use the current date as the default date in the picker
|
|
||||||
final Calendar c = Calendar.getInstance();
|
|
||||||
c.setTimeInMillis(System.currentTimeMillis());
|
|
||||||
int year = c.get(Calendar.YEAR);
|
|
||||||
int month = c.get(Calendar.MONTH);
|
|
||||||
int day = c.get(Calendar.DAY_OF_MONTH);
|
|
||||||
|
|
||||||
// Create a new instance of DatePickerDialog and return it
|
|
||||||
return new DatePickerDialog(getActivity(), this, year, month, day);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onDateSet(DatePicker view, int year, int month, int day) {
|
|
||||||
callback.onDatePick(year, month, day);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface DatePickerCallback {
|
|
||||||
void onDatePick(int year, int month, int day);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,97 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
*
|
|
||||||
* This file is part of FitoTrack
|
|
||||||
*
|
|
||||||
* FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.tadris.fitness.dialog;
|
|
||||||
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.NumberPicker;
|
|
||||||
|
|
||||||
import de.tadris.fitness.R;
|
|
||||||
|
|
||||||
public class DurationPickerDialogFragment {
|
|
||||||
|
|
||||||
public Activity context;
|
|
||||||
public DurationPickListener listener;
|
|
||||||
public long initialDuration;
|
|
||||||
|
|
||||||
public DurationPickerDialogFragment(Activity context, DurationPickListener listener, long initialDuration) {
|
|
||||||
this.context = context;
|
|
||||||
this.listener = listener;
|
|
||||||
this.initialDuration = initialDuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void show() {
|
|
||||||
final AlertDialog.Builder d = new AlertDialog.Builder(context);
|
|
||||||
d.setTitle(R.string.setDuration);
|
|
||||||
View v = context.getLayoutInflater().inflate(R.layout.dialog_duration_picker, null);
|
|
||||||
NumberPicker hours = v.findViewById(R.id.durationPickerHours);
|
|
||||||
hours.setFormatter(value -> value + " " + context.getString(R.string.timeHourShort));
|
|
||||||
hours.setMinValue(0);
|
|
||||||
hours.setMaxValue(24);
|
|
||||||
hours.setValue(getInitialHours());
|
|
||||||
|
|
||||||
NumberPicker minutes = v.findViewById(R.id.durationPickerMinutes);
|
|
||||||
minutes.setFormatter(value -> value + " " + context.getString(R.string.timeMinuteShort));
|
|
||||||
minutes.setMinValue(0);
|
|
||||||
minutes.setMaxValue(60);
|
|
||||||
minutes.setValue(getInitialMinutes());
|
|
||||||
|
|
||||||
NumberPicker seconds = v.findViewById(R.id.durationPickerSeconds);
|
|
||||||
seconds.setFormatter(value -> value + " " + context.getString(R.string.timeSecondsShort));
|
|
||||||
seconds.setMinValue(0);
|
|
||||||
seconds.setMaxValue(60);
|
|
||||||
seconds.setValue(getInitialSeconds());
|
|
||||||
|
|
||||||
d.setView(v);
|
|
||||||
|
|
||||||
d.setNegativeButton(R.string.cancel, null);
|
|
||||||
d.setPositiveButton(R.string.okay, (dialog, which) -> {
|
|
||||||
listener.onDurationPick(getMillisFromPick(hours.getValue(), minutes.getValue(), seconds.getValue()));
|
|
||||||
});
|
|
||||||
|
|
||||||
d.create().show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getInitialHours() {
|
|
||||||
return (int) (initialDuration / 1000 / 60 / 60);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getInitialMinutes() {
|
|
||||||
return (int) (initialDuration / 1000 / 60 % 60);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getInitialSeconds() {
|
|
||||||
return (int) (initialDuration / 1000 % 60);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static long getMillisFromPick(int hours, int minutes, int seconds) {
|
|
||||||
long secondInMillis = 1000L;
|
|
||||||
long minuteInMillis = secondInMillis * 60;
|
|
||||||
long hourInMillis = minuteInMillis * 60;
|
|
||||||
return hours * hourInMillis + minutes * minuteInMillis + seconds * secondInMillis;
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface DurationPickListener {
|
|
||||||
void onDurationPick(long duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
*
|
|
||||||
* This file is part of FitoTrack
|
|
||||||
*
|
|
||||||
* FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.tadris.fitness.dialog;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
|
|
||||||
import de.tadris.fitness.R;
|
|
||||||
import de.tadris.fitness.data.WorkoutType;
|
|
||||||
|
|
||||||
public class SelectWorkoutTypeDialog {
|
|
||||||
|
|
||||||
private Activity context;
|
|
||||||
private WorkoutTypeSelectListener listener;
|
|
||||||
private WorkoutType[] options;
|
|
||||||
|
|
||||||
public SelectWorkoutTypeDialog(Activity context, WorkoutTypeSelectListener listener) {
|
|
||||||
this.context = context;
|
|
||||||
this.listener = listener;
|
|
||||||
this.options = WorkoutType.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void show() {
|
|
||||||
AlertDialog.Builder builderSingle = new AlertDialog.Builder(context);
|
|
||||||
|
|
||||||
final ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(context, R.layout.select_dialog_singlechoice_material);
|
|
||||||
for (WorkoutType type : options) {
|
|
||||||
arrayAdapter.add(context.getString(type.title));
|
|
||||||
}
|
|
||||||
|
|
||||||
builderSingle.setAdapter(arrayAdapter, (dialog, which) -> listener.onSelectWorkoutType(options[which]));
|
|
||||||
builderSingle.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface WorkoutTypeSelectListener {
|
|
||||||
void onSelectWorkoutType(WorkoutType workoutType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
*
|
|
||||||
* This file is part of FitoTrack
|
|
||||||
*
|
|
||||||
* FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.tadris.fitness.dialog;
|
|
||||||
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.app.DialogFragment;
|
|
||||||
import android.app.TimePickerDialog;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.text.format.DateFormat;
|
|
||||||
import android.widget.TimePicker;
|
|
||||||
|
|
||||||
import java.util.Calendar;
|
|
||||||
|
|
||||||
public class TimePickerFragment extends DialogFragment implements TimePickerDialog.OnTimeSetListener {
|
|
||||||
|
|
||||||
public TimePickerCallback callback;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
||||||
// Use the current time as the default values for the picker
|
|
||||||
final Calendar c = Calendar.getInstance();
|
|
||||||
int hour = c.get(Calendar.HOUR_OF_DAY);
|
|
||||||
int minute = c.get(Calendar.MINUTE);
|
|
||||||
|
|
||||||
// Create a new instance of TimePickerDialog and return it
|
|
||||||
return new TimePickerDialog(getActivity(), this, hour, minute, DateFormat.is24HourFormat(getActivity()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
|
|
||||||
callback.onTimePick(hourOfDay, minute);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface TimePickerCallback {
|
|
||||||
void onTimePick(int hour, int minute);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,95 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
*
|
|
||||||
* This file is part of FitoTrack
|
|
||||||
*
|
|
||||||
* FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.tadris.fitness.export;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import de.tadris.fitness.Instance;
|
|
||||||
import de.tadris.fitness.R;
|
|
||||||
import de.tadris.fitness.data.AppDatabase;
|
|
||||||
import de.tadris.fitness.util.unit.UnitUtils;
|
|
||||||
|
|
||||||
public class BackupController {
|
|
||||||
|
|
||||||
private static final int VERSION = 1;
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
private final File output;
|
|
||||||
private final ExportStatusListener listener;
|
|
||||||
private AppDatabase database;
|
|
||||||
|
|
||||||
private FitoTrackDataContainer dataContainer;
|
|
||||||
|
|
||||||
public BackupController(Context context, File output, ExportStatusListener listener) {
|
|
||||||
this.context = context;
|
|
||||||
this.output = output;
|
|
||||||
this.listener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void exportData() throws IOException {
|
|
||||||
listener.onStatusChanged(0, context.getString(R.string.initialising));
|
|
||||||
init();
|
|
||||||
listener.onStatusChanged(20, context.getString(R.string.workouts));
|
|
||||||
saveWorkoutsToContainer();
|
|
||||||
listener.onStatusChanged(40, context.getString(R.string.locationData));
|
|
||||||
saveSamplesToContainer();
|
|
||||||
listener.onStatusChanged(60, context.getString(R.string.converting));
|
|
||||||
writeContainerToOutputFile();
|
|
||||||
listener.onStatusChanged(100, context.getString(R.string.finished));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init(){
|
|
||||||
database= Instance.getInstance(context).db;
|
|
||||||
UnitUtils.setUnit(context); // Ensure unit system is correct
|
|
||||||
newContainer();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void newContainer(){
|
|
||||||
dataContainer= new FitoTrackDataContainer();
|
|
||||||
dataContainer.setVersion(VERSION);
|
|
||||||
dataContainer.setWorkouts(new ArrayList<>());
|
|
||||||
dataContainer.setSamples(new ArrayList<>());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveWorkoutsToContainer(){
|
|
||||||
dataContainer.getWorkouts().addAll(Arrays.asList(database.workoutDao().getWorkouts()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveSamplesToContainer(){
|
|
||||||
dataContainer.getSamples().addAll(Arrays.asList(database.workoutDao().getSamples()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeContainerToOutputFile() throws IOException {
|
|
||||||
XmlMapper mapper= new XmlMapper();
|
|
||||||
mapper.writeValue(output, dataContainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ExportStatusListener{
|
|
||||||
void onStatusChanged(int progress, String action);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,111 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
*
|
|
||||||
* This file is part of FitoTrack
|
|
||||||
*
|
|
||||||
* FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.tadris.fitness.export;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.net.Uri;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonParser;
|
|
||||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import de.tadris.fitness.Instance;
|
|
||||||
import de.tadris.fitness.R;
|
|
||||||
import de.tadris.fitness.data.AppDatabase;
|
|
||||||
import de.tadris.fitness.data.Workout;
|
|
||||||
import de.tadris.fitness.data.WorkoutSample;
|
|
||||||
|
|
||||||
public class RestoreController {
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
private final Uri input;
|
|
||||||
private final ImportStatusListener listener;
|
|
||||||
private FitoTrackDataContainer dataContainer;
|
|
||||||
private final AppDatabase database;
|
|
||||||
|
|
||||||
public RestoreController(Context context, Uri input, ImportStatusListener listener) {
|
|
||||||
this.context = context;
|
|
||||||
this.input = input;
|
|
||||||
this.listener = listener;
|
|
||||||
this.database= Instance.getInstance(context).db;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void restoreData() throws IOException, UnsupportedVersionException{
|
|
||||||
listener.onStatusChanged(0, context.getString(R.string.loadingFile));
|
|
||||||
loadDataFromFile();
|
|
||||||
checkVersion();
|
|
||||||
restoreDatabase();
|
|
||||||
listener.onStatusChanged(100, context.getString(R.string.finished));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadDataFromFile() throws IOException {
|
|
||||||
XmlMapper xmlMapper = new XmlMapper();
|
|
||||||
xmlMapper.configure(JsonParser.Feature.IGNORE_UNDEFINED, true);
|
|
||||||
dataContainer = xmlMapper.readValue(context.getContentResolver().openInputStream(input), FitoTrackDataContainer.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkVersion() throws UnsupportedVersionException {
|
|
||||||
if (dataContainer.getVersion() != 1) {
|
|
||||||
throw new UnsupportedVersionException("Version Code" + dataContainer.getVersion() + " is unsupported!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void restoreDatabase(){
|
|
||||||
database.runInTransaction(() -> {
|
|
||||||
resetDatabase();
|
|
||||||
restoreWorkouts();
|
|
||||||
restoreSamples();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void resetDatabase(){
|
|
||||||
database.clearAllTables();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void restoreWorkouts(){
|
|
||||||
listener.onStatusChanged(60, context.getString(R.string.workouts));
|
|
||||||
if (dataContainer.getWorkouts() != null) {
|
|
||||||
for (Workout workout : dataContainer.getWorkouts()) {
|
|
||||||
database.workoutDao().insertWorkout(workout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void restoreSamples(){
|
|
||||||
listener.onStatusChanged(80, context.getString(R.string.locationData));
|
|
||||||
if (dataContainer.getSamples() != null) {
|
|
||||||
for (WorkoutSample sample : dataContainer.getSamples()) {
|
|
||||||
database.workoutDao().insertSample(sample);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ImportStatusListener{
|
|
||||||
void onStatusChanged(int progress, String action);
|
|
||||||
}
|
|
||||||
|
|
||||||
static class UnsupportedVersionException extends Exception {
|
|
||||||
UnsupportedVersionException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
package de.tadris.fitness.map;
|
package de.tadris.fitness.map;
|
||||||
|
|
||||||
|
import org.mapsforge.core.model.LatLong;
|
||||||
import org.mapsforge.map.android.graphics.AndroidGraphicFactory;
|
import org.mapsforge.map.android.graphics.AndroidGraphicFactory;
|
||||||
import org.mapsforge.map.android.util.AndroidUtil;
|
import org.mapsforge.map.android.util.AndroidUtil;
|
||||||
import org.mapsforge.map.android.view.MapView;
|
import org.mapsforge.map.android.view.MapView;
|
||||||
@ -30,19 +31,18 @@ import de.tadris.fitness.map.tilesource.FitoTrackTileSource;
|
|||||||
import de.tadris.fitness.map.tilesource.HumanitarianTileSource;
|
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.ThunderforestTileSource;
|
||||||
|
import de.tadris.fitness.map.tilesource.TileSources;
|
||||||
|
|
||||||
public class MapManager {
|
public class MapManager {
|
||||||
|
|
||||||
public static TileDownloadLayer setupMap(MapView mapView) {
|
public static TileDownloadLayer setupMap(MapView mapView, TileSources.Purpose purpose){
|
||||||
FitoTrackTileSource tileSource;
|
FitoTrackTileSource tileSource;
|
||||||
|
|
||||||
String chosenTileLayer= Instance.getInstance(mapView.getContext()).userPreferences.getMapStyle();
|
String chosenTileLayer= Instance.getInstance(mapView.getContext()).userPreferences.getMapStyle();
|
||||||
switch (chosenTileLayer){
|
switch (chosenTileLayer){
|
||||||
case "osm.humanitarian": tileSource= HumanitarianTileSource.INSTANCE; break;
|
case "osm.humanitarian": tileSource= HumanitarianTileSource.INSTANCE; break;
|
||||||
case "thunderforest.outdoors": tileSource= ThunderforestTileSource.OUTDOORS; break;
|
case "thunderforest.outdoors": tileSource= ThunderforestTileSource.OUTDOORS; break;
|
||||||
case "thunderforest.cycle":
|
case "thunderforest.cycle": tileSource= ThunderforestTileSource.CYLE_MAP; break;
|
||||||
tileSource = ThunderforestTileSource.CYCLE_MAP;
|
|
||||||
break;
|
|
||||||
default: tileSource= MapnikTileSource.INSTANCE; break; // Inclusive "osm.mapnik"
|
default: tileSource= MapnikTileSource.INSTANCE; break; // Inclusive "osm.mapnik"
|
||||||
}
|
}
|
||||||
tileSource.setUserAgent("mapsforge-android");
|
tileSource.setUserAgent("mapsforge-android");
|
||||||
@ -62,6 +62,7 @@ public class MapManager {
|
|||||||
mapView.getLayerManager().redrawLayers();
|
mapView.getLayerManager().redrawLayers();
|
||||||
|
|
||||||
mapView.setZoomLevel((byte) 18);
|
mapView.setZoomLevel((byte) 18);
|
||||||
|
mapView.setCenter(new LatLong(52, 13));
|
||||||
|
|
||||||
return downloadLayer;
|
return downloadLayer;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -30,7 +30,7 @@ import de.tadris.fitness.data.WorkoutSample;
|
|||||||
|
|
||||||
public class WorkoutLayer extends Polyline {
|
public class WorkoutLayer extends Polyline {
|
||||||
|
|
||||||
private static Paint getDEFAULT_PAINT_STROKE(int color) {
|
public static Paint getDEFAULT_PAINT_STROKE(int color){
|
||||||
Paint paint= AndroidGraphicFactory.INSTANCE.createPaint();
|
Paint paint= AndroidGraphicFactory.INSTANCE.createPaint();
|
||||||
paint.setStyle(Style.STROKE);
|
paint.setStyle(Style.STROKE);
|
||||||
paint.setColor(color);
|
paint.setColor(color);
|
||||||
@ -38,13 +38,13 @@ public class WorkoutLayer extends Polyline {
|
|||||||
return paint;
|
return paint;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final List<WorkoutSample> samples;
|
List<WorkoutSample> samples;
|
||||||
|
|
||||||
public WorkoutLayer(List<WorkoutSample> samples, int color) {
|
public WorkoutLayer(List<WorkoutSample> samples, int color) {
|
||||||
this(getDEFAULT_PAINT_STROKE(color), samples);
|
this(getDEFAULT_PAINT_STROKE(color), samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
private WorkoutLayer(Paint paintStroke, List<WorkoutSample> samples) {
|
public WorkoutLayer(Paint paintStroke, List<WorkoutSample> samples) {
|
||||||
super(paintStroke, AndroidGraphicFactory.INSTANCE);
|
super(paintStroke, AndroidGraphicFactory.INSTANCE);
|
||||||
this.samples = samples;
|
this.samples = samples;
|
||||||
init();
|
init();
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -23,7 +23,7 @@ import org.mapsforge.map.layer.download.tilesource.AbstractTileSource;
|
|||||||
|
|
||||||
public abstract class FitoTrackTileSource extends AbstractTileSource {
|
public abstract class FitoTrackTileSource extends AbstractTileSource {
|
||||||
|
|
||||||
FitoTrackTileSource(String[] hostNames, int port) {
|
public FitoTrackTileSource(String[] hostNames, int port) {
|
||||||
super(hostNames, port);
|
super(hostNames, port);
|
||||||
defaultTimeToLive = 8279000;
|
defaultTimeToLive = 8279000;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -26,7 +26,7 @@ import java.net.URL;
|
|||||||
|
|
||||||
public class HumanitarianTileSource extends FitoTrackTileSource {
|
public class HumanitarianTileSource extends FitoTrackTileSource {
|
||||||
|
|
||||||
public static final HumanitarianTileSource INSTANCE = new HumanitarianTileSource(new String[]{"tile-a.openstreetmap.fr", "tile-b.openstreetmap.fr", "tile-c.openstreetmap.fr"}, 443);
|
public static HumanitarianTileSource INSTANCE= new HumanitarianTileSource(new String[]{"tile-a.openstreetmap.fr", "tile-b.openstreetmap.fr", "tile-c.openstreetmap.fr"}, 443);
|
||||||
|
|
||||||
private static final int PARALLEL_REQUESTS_LIMIT = 8;
|
private static final int PARALLEL_REQUESTS_LIMIT = 8;
|
||||||
private static final String PROTOCOL = "https";
|
private static final String PROTOCOL = "https";
|
||||||
@ -34,7 +34,7 @@ public class HumanitarianTileSource extends FitoTrackTileSource {
|
|||||||
private static final int ZOOM_LEVEL_MIN = 0;
|
private static final int ZOOM_LEVEL_MIN = 0;
|
||||||
private static final String NAME = "Humanitarian";
|
private static final String NAME = "Humanitarian";
|
||||||
|
|
||||||
private HumanitarianTileSource(String[] hostNames, int port) {
|
public HumanitarianTileSource(String[] hostNames, int port) {
|
||||||
super(hostNames, port);
|
super(hostNames, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -30,11 +30,11 @@ public class MapnikTileSource extends FitoTrackTileSource {
|
|||||||
"a.tile.openstreetmap.org", "b.tile.openstreetmap.org", "c.tile.openstreetmap.org"}, 443);
|
"a.tile.openstreetmap.org", "b.tile.openstreetmap.org", "c.tile.openstreetmap.org"}, 443);
|
||||||
private static final int PARALLEL_REQUESTS_LIMIT = 8;
|
private static final int PARALLEL_REQUESTS_LIMIT = 8;
|
||||||
private static final String PROTOCOL = "https";
|
private static final String PROTOCOL = "https";
|
||||||
private static final int ZOOM_LEVEL_MAX = 19;
|
private static final int ZOOM_LEVEL_MAX = 18;
|
||||||
private static final int ZOOM_LEVEL_MIN = 0;
|
private static final int ZOOM_LEVEL_MIN = 0;
|
||||||
private static final String NAME = "OSM Mapnik";
|
private static final String NAME = "OSM Mapnik";
|
||||||
|
|
||||||
private MapnikTileSource(String[] hostNames, int port) {
|
public MapnikTileSource(String[] hostNames, int port) {
|
||||||
super(hostNames, port);
|
super(hostNames, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -26,19 +26,19 @@ import java.net.URL;
|
|||||||
|
|
||||||
public class ThunderforestTileSource extends FitoTrackTileSource{
|
public class ThunderforestTileSource extends FitoTrackTileSource{
|
||||||
|
|
||||||
private static final String API_KEY = "87b07337e42c405db6d8d39b1c0c179e";
|
public static final String API_KEY= "87b07337e42c405db6d8d39b1c0c179e";
|
||||||
|
|
||||||
public static final ThunderforestTileSource OUTDOORS = new ThunderforestTileSource("outdoors", "Outdoor");
|
public static final ThunderforestTileSource OUTDOORS = new ThunderforestTileSource("outdoors", "Outdoor");
|
||||||
public static final ThunderforestTileSource CYCLE_MAP = new ThunderforestTileSource("cycle", "Cycle Map");
|
public static final ThunderforestTileSource CYLE_MAP = new ThunderforestTileSource("cycle", "Cycle Map");
|
||||||
private static final int PARALLEL_REQUESTS_LIMIT = 8;
|
private static final int PARALLEL_REQUESTS_LIMIT = 8;
|
||||||
private static final String PROTOCOL = "https";
|
private static final String PROTOCOL = "https";
|
||||||
private static final int ZOOM_LEVEL_MAX = 19;
|
private static final int ZOOM_LEVEL_MAX = 22;
|
||||||
private static final int ZOOM_LEVEL_MIN = 0;
|
private static final int ZOOM_LEVEL_MIN = 0;
|
||||||
|
|
||||||
private final String mapName;
|
private String mapName;
|
||||||
private final String name;
|
private String name;
|
||||||
|
|
||||||
private ThunderforestTileSource(String mapName, String name) {
|
public ThunderforestTileSource(String mapName, String name) {
|
||||||
super(new String[]{"tile.thunderforest.com"}, 443);
|
super(new String[]{"tile.thunderforest.com"}, 443);
|
||||||
this.mapName = mapName;
|
this.mapName = mapName;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -17,26 +17,17 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.tadris.fitness.recording.announcement;
|
package de.tadris.fitness.map.tilesource;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
|
|
||||||
public enum AnnouncementMode {
|
public class TileSources {
|
||||||
|
|
||||||
ALWAYS,
|
public static FitoTrackTileSource[] tileSources= new FitoTrackTileSource[]{
|
||||||
HEADPHONES;
|
MapnikTileSource.INSTANCE, HumanitarianTileSource.INSTANCE, ThunderforestTileSource.OUTDOORS, ThunderforestTileSource.CYLE_MAP
|
||||||
|
};
|
||||||
|
|
||||||
static AnnouncementMode getCurrentMode(Context context) {
|
public enum Purpose{
|
||||||
String mode = PreferenceManager.getDefaultSharedPreferences(context).getString("announcementMode", "headphones");
|
DEFAULT, OUTDOOR, CYCLING
|
||||||
assert mode != null;
|
|
||||||
switch (mode) {
|
|
||||||
case "always":
|
|
||||||
return ALWAYS;
|
|
||||||
default:
|
|
||||||
case "headphones":
|
|
||||||
return HEADPHONES;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -22,12 +22,12 @@ package de.tadris.fitness.osm;
|
|||||||
import de.tadris.fitness.data.WorkoutSample;
|
import de.tadris.fitness.data.WorkoutSample;
|
||||||
import de.westnordost.osmapi.map.data.LatLon;
|
import de.westnordost.osmapi.map.data.LatLon;
|
||||||
|
|
||||||
class GpsTraceLatLong implements LatLon {
|
public class GpsTraceLatLong implements LatLon {
|
||||||
|
|
||||||
private final double latitude;
|
private final double latitude;
|
||||||
private final double longitude;
|
private final double longitude;
|
||||||
|
|
||||||
private GpsTraceLatLong(double latitude, double longitude) {
|
public GpsTraceLatLong(double latitude, double longitude) {
|
||||||
this.latitude = latitude;
|
this.latitude = latitude;
|
||||||
this.longitude = longitude;
|
this.longitude = longitude;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -39,14 +39,14 @@ import oauth.signpost.exception.OAuthException;
|
|||||||
|
|
||||||
public class OAuthAuthentication {
|
public class OAuthAuthentication {
|
||||||
|
|
||||||
private final OAuthConsumer oAuthConsumer = OAuthUrlProvider.getDefaultConsumer();
|
private OAuthConsumer oAuthConsumer= OAuthUrlProvider.getDefaultConsumer();
|
||||||
private final OAuthProvider oAuthProvider = OAuthUrlProvider.getDefaultProvider();
|
private OAuthProvider oAuthProvider= OAuthUrlProvider.getDefaultProvider();
|
||||||
|
|
||||||
private final Handler handler;
|
private Handler handler;
|
||||||
private final Activity activity;
|
private Activity activity;
|
||||||
private final ProgressDialogController dialogController;
|
private ProgressDialogController dialogController;
|
||||||
private final SharedPreferences preferences;
|
private SharedPreferences preferences;
|
||||||
private final OAuthAuthenticationListener listener;
|
private OAuthAuthenticationListener listener;
|
||||||
|
|
||||||
public OAuthAuthentication(Handler handler, Activity activity, OAuthAuthenticationListener listener) {
|
public OAuthAuthentication(Handler handler, Activity activity, OAuthAuthenticationListener listener) {
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
@ -118,12 +118,12 @@ public class OAuthAuthentication {
|
|||||||
.putBoolean("authenticated", true).apply();
|
.putBoolean("authenticated", true).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearAccessToken() {
|
public void clearAccessToken(){
|
||||||
preferences.edit().putBoolean("authenticated", false).apply();
|
preferences.edit().putBoolean("authenticated", false).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void retrieveAccessToken(String verificationCode){
|
private void retrieveAccessToken(String verificationCode){
|
||||||
handler.post(dialogController::show);
|
handler.post(() -> dialogController.show());
|
||||||
try{
|
try{
|
||||||
oAuthProvider.retrieveAccessToken(oAuthConsumer, verificationCode);
|
oAuthProvider.retrieveAccessToken(oAuthConsumer, verificationCode);
|
||||||
handler.post(() -> {
|
handler.post(() -> {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -24,7 +24,7 @@ import oauth.signpost.OAuthProvider;
|
|||||||
import oauth.signpost.basic.DefaultOAuthConsumer;
|
import oauth.signpost.basic.DefaultOAuthConsumer;
|
||||||
import oauth.signpost.basic.DefaultOAuthProvider;
|
import oauth.signpost.basic.DefaultOAuthProvider;
|
||||||
|
|
||||||
class OAuthUrlProvider {
|
public class OAuthUrlProvider {
|
||||||
|
|
||||||
static private final String CONSUMER_KEY= "jFL9grFmAo5ZS720YDDRXdSOb7F0IZQf9lnY1PHq";
|
static private final String CONSUMER_KEY= "jFL9grFmAo5ZS720YDDRXdSOb7F0IZQf9lnY1PHq";
|
||||||
static private final String CONSUMER_SECRET= "oH969vYW60fZLco6E09UQl3uFXqjl4siQbOL0q9q";
|
static private final String CONSUMER_SECRET= "oH969vYW60fZLco6E09UQl3uFXqjl4siQbOL0q9q";
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -24,8 +24,6 @@ import android.os.Handler;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.StringRes;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@ -36,7 +34,6 @@ import de.tadris.fitness.data.Workout;
|
|||||||
import de.tadris.fitness.data.WorkoutSample;
|
import de.tadris.fitness.data.WorkoutSample;
|
||||||
import de.tadris.fitness.view.ProgressDialogController;
|
import de.tadris.fitness.view.ProgressDialogController;
|
||||||
import de.westnordost.osmapi.OsmConnection;
|
import de.westnordost.osmapi.OsmConnection;
|
||||||
import de.westnordost.osmapi.common.errors.OsmAuthorizationException;
|
|
||||||
import de.westnordost.osmapi.traces.GpsTraceDetails;
|
import de.westnordost.osmapi.traces.GpsTraceDetails;
|
||||||
import de.westnordost.osmapi.traces.GpsTracesDao;
|
import de.westnordost.osmapi.traces.GpsTracesDao;
|
||||||
import de.westnordost.osmapi.traces.GpsTrackpoint;
|
import de.westnordost.osmapi.traces.GpsTrackpoint;
|
||||||
@ -46,15 +43,15 @@ public class OsmTraceUploader {
|
|||||||
|
|
||||||
private static final int CUT_DISTANCE= 300;
|
private static final int CUT_DISTANCE= 300;
|
||||||
|
|
||||||
private final Activity activity;
|
private Activity activity;
|
||||||
private final Handler handler;
|
private Handler handler;
|
||||||
private final Workout workout;
|
private Workout workout;
|
||||||
private final List<WorkoutSample> samples;
|
private List<WorkoutSample> samples;
|
||||||
private final GpsTraceDetails.Visibility visibility;
|
private GpsTraceDetails.Visibility visibility;
|
||||||
private final OAuthConsumer consumer;
|
private OAuthConsumer consumer;
|
||||||
private final boolean cut;
|
private boolean cut;
|
||||||
private final ProgressDialogController dialogController;
|
private ProgressDialogController dialogController;
|
||||||
private final String description;
|
private String description;
|
||||||
|
|
||||||
public OsmTraceUploader(Activity activity, Handler handler, Workout workout, List<WorkoutSample> samples, GpsTraceDetails.Visibility visibility, OAuthConsumer consumer, boolean cut, String description) {
|
public OsmTraceUploader(Activity activity, Handler handler, Workout workout, List<WorkoutSample> samples, GpsTraceDetails.Visibility visibility, OAuthConsumer consumer, boolean cut, String description) {
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
@ -88,19 +85,12 @@ public class OsmTraceUploader {
|
|||||||
|
|
||||||
public void upload(){
|
public void upload(){
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try{
|
||||||
executeTask();
|
executeTask();
|
||||||
}catch (Exception e){
|
}catch (Exception e){
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
handler.post(() -> {
|
handler.post(() -> {
|
||||||
@StringRes int textRes = R.string.uploadFailed;
|
Toast.makeText(activity, R.string.uploadFailed, Toast.LENGTH_LONG).show();
|
||||||
|
|
||||||
if (e instanceof OsmAuthorizationException) {
|
|
||||||
textRes = R.string.uploadFailedOsmNotAuthorized;
|
|
||||||
new OAuthAuthentication(handler, activity, null).clearAccessToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
Toast.makeText(activity, textRes, Toast.LENGTH_LONG).show();
|
|
||||||
dialogController.cancel();
|
dialogController.cancel();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -108,7 +98,7 @@ public class OsmTraceUploader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void executeTask(){
|
private void executeTask(){
|
||||||
handler.post(dialogController::show);
|
handler.post(() -> dialogController.show());
|
||||||
setProgress(0);
|
setProgress(0);
|
||||||
if(cut){ cut(); }
|
if(cut){ cut(); }
|
||||||
setProgress(20);
|
setProgress(20);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -51,9 +51,9 @@ public class LocationListener extends Service {
|
|||||||
private static final int LOCATION_INTERVAL = 1000;
|
private static final int LOCATION_INTERVAL = 1000;
|
||||||
|
|
||||||
private class LocationChangedListener implements android.location.LocationListener {
|
private class LocationChangedListener implements android.location.LocationListener {
|
||||||
final Location mLastLocation;
|
Location mLastLocation;
|
||||||
|
|
||||||
LocationChangedListener(String provider) {
|
public LocationChangedListener(String provider) {
|
||||||
Log.i(TAG, "LocationListener " + provider);
|
Log.i(TAG, "LocationListener " + provider);
|
||||||
mLastLocation = new Location(provider);
|
mLastLocation = new Location(provider);
|
||||||
}
|
}
|
||||||
@ -83,7 +83,7 @@ public class LocationListener extends Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final LocationChangedListener gpsListener = new LocationChangedListener(LocationManager.GPS_PROVIDER);
|
LocationChangedListener gpsListener= new LocationChangedListener(LocationManager.GPS_PROVIDER);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBinder onBind(Intent arg0) {
|
public IBinder onBind(Intent arg0) {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -31,21 +31,32 @@ import java.util.List;
|
|||||||
|
|
||||||
import de.tadris.fitness.Instance;
|
import de.tadris.fitness.Instance;
|
||||||
import de.tadris.fitness.data.Workout;
|
import de.tadris.fitness.data.Workout;
|
||||||
|
import de.tadris.fitness.data.WorkoutManager;
|
||||||
import de.tadris.fitness.data.WorkoutSample;
|
import de.tadris.fitness.data.WorkoutSample;
|
||||||
import de.tadris.fitness.data.WorkoutType;
|
|
||||||
import de.tadris.fitness.util.CalorieCalculator;
|
import de.tadris.fitness.util.CalorieCalculator;
|
||||||
|
|
||||||
public class WorkoutRecorder implements LocationListener.LocationChangeListener {
|
public class WorkoutRecorder implements LocationListener.LocationChangeListener {
|
||||||
|
|
||||||
|
private static int getMinDistance(String workoutType){
|
||||||
|
switch (workoutType){
|
||||||
|
case Workout.WORKOUT_TYPE_HIKING:
|
||||||
|
case Workout.WORKOUT_TYPE_RUNNING:
|
||||||
|
return 8;
|
||||||
|
case Workout.WORKOUT_TYPE_CYCLING:
|
||||||
|
return 15;
|
||||||
|
default: return 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final int PAUSE_TIME= 10000;
|
private static final int PAUSE_TIME= 10000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Time after which the workout is stopped and saved automatically because there is no activity anymore
|
* Time after which the workout is stopped and saved automatically because there is no activity anymore
|
||||||
*/
|
*/
|
||||||
private static final int AUTO_STOP_TIMEOUT= 1000*60*60*20; // 20 minutes
|
private static final int AUTO_STOP_TIMEOUT= 1000*60*60*20;
|
||||||
|
|
||||||
private final Context context;
|
private Context context;
|
||||||
private final Workout workout;
|
private Workout workout;
|
||||||
private RecordingState state;
|
private RecordingState state;
|
||||||
private final List<WorkoutSample> samples= new ArrayList<>();
|
private final List<WorkoutSample> samples= new ArrayList<>();
|
||||||
private long time= 0;
|
private long time= 0;
|
||||||
@ -54,26 +65,25 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
|
|||||||
private long lastPause= 0;
|
private long lastPause= 0;
|
||||||
private long lastSampleTime= 0;
|
private long lastSampleTime= 0;
|
||||||
private double distance= 0;
|
private double distance= 0;
|
||||||
private boolean hasBegun = false;
|
private boolean hasBegan = false;
|
||||||
|
|
||||||
private static final double SIGNAL_BAD_THRESHOLD= 20; // In meters
|
private static final double SIGNAL_BAD_THRESHOLD= 20; // In meters
|
||||||
private static final int SIGNAL_LOST_THRESHOLD= 10000; // In milliseconds
|
private static final int SIGNAL_LOST_THRESHOLD= 10000; // In milliseconds
|
||||||
private Location lastFix= null;
|
private Location lastFix= null;
|
||||||
private final WorkoutRecorderListener workoutRecorderListener;
|
private WorkoutRecorderListener workoutRecorderListener;
|
||||||
private GpsState gpsState= GpsState.SIGNAL_LOST;
|
private GpsState gpsState= GpsState.SIGNAL_LOST;
|
||||||
|
|
||||||
public WorkoutRecorder(Context context, WorkoutType workoutType, WorkoutRecorderListener workoutRecorderListener) {
|
public WorkoutRecorder(Context context, String workoutType, WorkoutRecorderListener workoutRecorderListener) {
|
||||||
this.context= context;
|
this.context= context;
|
||||||
this.state= RecordingState.IDLE;
|
this.state= RecordingState.IDLE;
|
||||||
this.workoutRecorderListener = workoutRecorderListener;
|
this.workoutRecorderListener = workoutRecorderListener;
|
||||||
|
|
||||||
this.workout= new Workout();
|
this.workout= new Workout();
|
||||||
workout.edited = false;
|
|
||||||
|
|
||||||
// Default values
|
// Default values
|
||||||
this.workout.comment= "";
|
this.workout.comment= "";
|
||||||
|
|
||||||
this.workout.setWorkoutType(workoutType);
|
this.workout.workoutType= workoutType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start(){
|
public void start(){
|
||||||
@ -110,7 +120,7 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
|
|||||||
workoutRecorderListener.onAutoStop();
|
workoutRecorderListener.onAutoStop();
|
||||||
}
|
}
|
||||||
}else if(timeDiff > PAUSE_TIME){
|
}else if(timeDiff > PAUSE_TIME){
|
||||||
if (state == RecordingState.RUNNING && gpsState != GpsState.SIGNAL_LOST) {
|
if(state == RecordingState.RUNNING){
|
||||||
pause();
|
pause();
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
@ -125,7 +135,7 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
|
|||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}, "WorkoutWatchdog").start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkSignalState(){
|
private void checkSignalState(){
|
||||||
@ -155,7 +165,7 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pause() {
|
public void pause(){
|
||||||
if(state == RecordingState.RUNNING){
|
if(state == RecordingState.RUNNING){
|
||||||
Log.i("Recorder", "Pause");
|
Log.i("Recorder", "Pause");
|
||||||
state= RecordingState.PAUSED;
|
state= RecordingState.PAUSED;
|
||||||
@ -183,7 +193,7 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
|
|||||||
}
|
}
|
||||||
Log.i("Recorder", "Save");
|
Log.i("Recorder", "Save");
|
||||||
synchronized (samples){
|
synchronized (samples){
|
||||||
new WorkoutSaver(context, workout, samples).saveWorkout();
|
WorkoutManager.insertWorkout(context, workout, samples);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,58 +209,53 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
|
|||||||
if(isActive()){
|
if(isActive()){
|
||||||
double distance= 0;
|
double distance= 0;
|
||||||
if(getSampleCount() > 0){
|
if(getSampleCount() > 0){
|
||||||
// Checks whether the minimum distance to last sample was reached
|
|
||||||
// and if the time difference to the last sample is too small
|
|
||||||
synchronized (samples){
|
synchronized (samples){
|
||||||
WorkoutSample lastSample= samples.get(samples.size() - 1);
|
WorkoutSample lastSample= samples.get(samples.size() - 1);
|
||||||
distance= LocationListener.locationToLatLong(location).sphericalDistance(new LatLong(lastSample.lat, lastSample.lon));
|
distance= LocationListener.locationToLatLong(location).sphericalDistance(new LatLong(lastSample.lat, lastSample.lon));
|
||||||
long timediff= lastSample.absoluteTime - location.getTime();
|
long timediff= lastSample.absoluteTime - location.getTime();
|
||||||
if (distance < workout.getWorkoutType().minDistance && timediff < 500) {
|
if(distance < getMinDistance(workout.workoutType) && timediff < 500){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastSampleTime= System.currentTimeMillis();
|
lastSampleTime= System.currentTimeMillis();
|
||||||
if(state == RecordingState.RUNNING && location.getTime() > workout.start){
|
if(state == RecordingState.RUNNING && location.getTime() > workout.start){
|
||||||
if(samples.size() == 2 && !hasBegun){
|
if(samples.size() == 2 && !hasBegan){
|
||||||
initialClearValues();
|
lastResume= System.currentTimeMillis();
|
||||||
hasBegun = true; // Do not clear a second time
|
workout.start= System.currentTimeMillis();
|
||||||
|
lastPause= 0;
|
||||||
|
time= 0;
|
||||||
|
pauseTime= 0;
|
||||||
|
this.distance= 0;
|
||||||
|
samples.clear();
|
||||||
|
|
||||||
|
hasBegan = true; // Do not clear a second time
|
||||||
}
|
}
|
||||||
this.distance+= distance;
|
this.distance+= distance;
|
||||||
addToSamples(location);
|
WorkoutSample sample= new WorkoutSample();
|
||||||
|
sample.lat= location.getLatitude();
|
||||||
|
sample.lon= location.getLongitude();
|
||||||
|
sample.elevation= location.getAltitude();
|
||||||
|
sample.speed= location.getSpeed();
|
||||||
|
sample.relativeTime= location.getTime() - workout.start - pauseTime;
|
||||||
|
sample.absoluteTime= location.getTime();
|
||||||
|
if(Instance.getInstance(context).pressureAvailable){
|
||||||
|
sample.tmpPressure= Instance.getInstance(context).lastPressure;
|
||||||
|
}else{
|
||||||
|
sample.tmpPressure= -1;
|
||||||
|
}
|
||||||
|
synchronized (samples){
|
||||||
|
samples.add(sample);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addToSamples(Location location){
|
/**
|
||||||
WorkoutSample sample= new WorkoutSample();
|
* Returns the distance in meters
|
||||||
sample.lat= location.getLatitude();
|
*/
|
||||||
sample.lon= location.getLongitude();
|
public int getDistance(){
|
||||||
sample.elevation= location.getAltitude();
|
|
||||||
sample.speed= location.getSpeed();
|
|
||||||
sample.relativeTime= location.getTime() - workout.start - pauseTime;
|
|
||||||
sample.absoluteTime= location.getTime();
|
|
||||||
if(Instance.getInstance(context).pressureAvailable){
|
|
||||||
sample.tmpPressure= Instance.getInstance(context).lastPressure;
|
|
||||||
}else{
|
|
||||||
sample.tmpPressure= -1;
|
|
||||||
}
|
|
||||||
synchronized (samples){
|
|
||||||
samples.add(sample);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initialClearValues(){
|
|
||||||
lastResume= System.currentTimeMillis();
|
|
||||||
workout.start= System.currentTimeMillis();
|
|
||||||
lastPause= 0;
|
|
||||||
time= 0;
|
|
||||||
pauseTime= 0;
|
|
||||||
this.distance= 0;
|
|
||||||
samples.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getDistanceInMeters() {
|
|
||||||
return (int)distance;
|
return (int)distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,7 +312,7 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
|
|||||||
SIGNAL_OKAY(Color.GREEN),
|
SIGNAL_OKAY(Color.GREEN),
|
||||||
SIGNAL_BAD(Color.YELLOW);
|
SIGNAL_BAD(Color.YELLOW);
|
||||||
|
|
||||||
public final int color;
|
public int color;
|
||||||
|
|
||||||
GpsState(int color) {
|
GpsState(int color) {
|
||||||
this.color = color;
|
this.color = color;
|
||||||
|
|||||||
@ -1,209 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
*
|
|
||||||
* This file is part of FitoTrack
|
|
||||||
*
|
|
||||||
* FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.tadris.fitness.recording;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.hardware.SensorManager;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import de.tadris.fitness.Instance;
|
|
||||||
import de.tadris.fitness.data.AppDatabase;
|
|
||||||
import de.tadris.fitness.data.Workout;
|
|
||||||
import de.tadris.fitness.data.WorkoutSample;
|
|
||||||
import de.tadris.fitness.util.AltitudeCorrection;
|
|
||||||
import de.tadris.fitness.util.CalorieCalculator;
|
|
||||||
|
|
||||||
class WorkoutSaver {
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
private final Workout workout;
|
|
||||||
private final List<WorkoutSample> samples;
|
|
||||||
private final AppDatabase db;
|
|
||||||
|
|
||||||
public WorkoutSaver(Context context, Workout workout, List<WorkoutSample> samples) {
|
|
||||||
this.context = context;
|
|
||||||
this.workout = workout;
|
|
||||||
this.samples = samples;
|
|
||||||
db= Instance.getInstance(context).db;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void saveWorkout(){
|
|
||||||
setIds();
|
|
||||||
clearSamplesWithSameTime();
|
|
||||||
setSimpleValues();
|
|
||||||
setTopSpeed();
|
|
||||||
|
|
||||||
setElevation();
|
|
||||||
setAscentAndDescent();
|
|
||||||
|
|
||||||
setCalories();
|
|
||||||
|
|
||||||
storeInDatabase();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setIds(){
|
|
||||||
workout.id= System.currentTimeMillis();
|
|
||||||
int i= 0;
|
|
||||||
for(WorkoutSample sample : samples) {
|
|
||||||
i++;
|
|
||||||
sample.id = workout.id + i;
|
|
||||||
sample.workoutId = workout.id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void clearSamplesWithSameTime(){
|
|
||||||
for(int i= samples.size()-2; i >= 0; i--){
|
|
||||||
WorkoutSample sample= samples.get(i);
|
|
||||||
WorkoutSample lastSample= samples.get(i+1);
|
|
||||||
if(sample.absoluteTime == lastSample.absoluteTime){
|
|
||||||
samples.remove(lastSample);
|
|
||||||
Log.i("WorkoutManager", "Removed samples at " + sample.absoluteTime + " rel: " + sample.relativeTime + "; " + lastSample.relativeTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setSimpleValues(){
|
|
||||||
double length= 0;
|
|
||||||
for(int i= 1; i < samples.size(); i++){
|
|
||||||
double sampleLength= samples.get(i - 1).toLatLong().sphericalDistance(samples.get(i).toLatLong());
|
|
||||||
length+= sampleLength;
|
|
||||||
}
|
|
||||||
workout.length= (int)length;
|
|
||||||
workout.avgSpeed= ((double) workout.length) / ((double) workout.duration / 1000);
|
|
||||||
workout.avgPace= ((double)workout.duration / 1000 / 60) / ((double) workout.length / 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setTopSpeed(){
|
|
||||||
double topSpeed= 0;
|
|
||||||
for(WorkoutSample sample : samples){
|
|
||||||
if(sample.speed > topSpeed){
|
|
||||||
topSpeed= sample.speed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
workout.topSpeed= topSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setElevation() {
|
|
||||||
setCorrectedElevation();
|
|
||||||
setPressureElevation();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setCorrectedElevation() {
|
|
||||||
// Please see the AltitudeCorrection.java for the reason of this
|
|
||||||
try {
|
|
||||||
int lat = (int) Math.round(samples.get(0).lat);
|
|
||||||
int lon = (int) Math.round(samples.get(0).lon);
|
|
||||||
AltitudeCorrection correction = new AltitudeCorrection(context, lat, lon);
|
|
||||||
for (WorkoutSample sample : samples) {
|
|
||||||
sample.elevation = correction.getHeightOverSeaLevel(sample.elevation);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
// If we can't read the file, we cannot correct the values
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setPressureElevation() {
|
|
||||||
boolean pressureDataAvailable= samples.get(0).tmpPressure != -1;
|
|
||||||
|
|
||||||
if(!pressureDataAvailable){
|
|
||||||
// Because pressure data isn't available we just use the use GPS elevation
|
|
||||||
// in WorkoutSample.elevation which was already set
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
double avgElevation= getAverageElevation();
|
|
||||||
double avgPressure= getAveragePressure();
|
|
||||||
|
|
||||||
for(int i= 0; i < samples.size(); i++){
|
|
||||||
WorkoutSample sample= samples.get(i);
|
|
||||||
|
|
||||||
// Altitude Difference to Average Elevation in meters
|
|
||||||
float altitude_difference =
|
|
||||||
SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, sample.tmpPressure) -
|
|
||||||
SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, (float) avgPressure);
|
|
||||||
sample.elevation= avgElevation + altitude_difference;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private double getAverageElevation(){
|
|
||||||
return getAverageElevation(samples);
|
|
||||||
}
|
|
||||||
|
|
||||||
private double getAverageElevation(List<WorkoutSample> samples){
|
|
||||||
double elevationSum= 0; // Sum of elevation
|
|
||||||
for(WorkoutSample sample : samples){
|
|
||||||
elevationSum+= sample.elevation;
|
|
||||||
}
|
|
||||||
|
|
||||||
return elevationSum / samples.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
private double getAveragePressure(){
|
|
||||||
double pressureSum= 0;
|
|
||||||
for(WorkoutSample sample : samples){
|
|
||||||
pressureSum+= sample.tmpPressure;
|
|
||||||
}
|
|
||||||
return pressureSum / samples.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setAscentAndDescent(){
|
|
||||||
workout.ascent = 0;
|
|
||||||
workout.descent = 0;
|
|
||||||
|
|
||||||
// First calculate a floating average to eliminate pressure noise to influence our ascent/descent
|
|
||||||
int range = 7;
|
|
||||||
for(int i= 0; i < samples.size(); i++){
|
|
||||||
int minIndex = Math.max(i - range, 0);
|
|
||||||
int maxIndex = Math.min(i + range, samples.size() - 1);
|
|
||||||
samples.get(i).tmpElevation = getAverageElevation(samples.subList(minIndex, maxIndex));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now sum up the ascent/descent
|
|
||||||
for(int i= 0; i < samples.size(); i++) {
|
|
||||||
WorkoutSample sample = samples.get(i);
|
|
||||||
sample.elevation= sample.tmpElevation;
|
|
||||||
if(i >= 1){
|
|
||||||
WorkoutSample lastSample= samples.get(i-1);
|
|
||||||
double diff= sample.elevation - lastSample.elevation;
|
|
||||||
if(diff > 0){
|
|
||||||
// If this sample is higher than the last one, add difference to ascent
|
|
||||||
workout.ascent += diff;
|
|
||||||
}else{
|
|
||||||
// If this sample is lower than the last one, add difference to descent
|
|
||||||
workout.descent += Math.abs(diff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setCalories() {
|
|
||||||
// Ascent has to be set previously
|
|
||||||
workout.calorie = CalorieCalculator.calculateCalories(workout, Instance.getInstance(context).userPreferences.getUserWeight());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void storeInDatabase(){
|
|
||||||
db.workoutDao().insertWorkoutAndSamples(workout, samples.toArray(new WorkoutSample[0]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
*
|
|
||||||
* This file is part of FitoTrack
|
|
||||||
*
|
|
||||||
* FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.tadris.fitness.recording.announcement;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
|
|
||||||
import androidx.annotation.StringRes;
|
|
||||||
|
|
||||||
import de.tadris.fitness.recording.WorkoutRecorder;
|
|
||||||
|
|
||||||
public abstract class Announcement {
|
|
||||||
|
|
||||||
private Context context;
|
|
||||||
|
|
||||||
Announcement(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("announcement_" + getId(), isEnabledByDefault());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getString(@StringRes int resId) {
|
|
||||||
return context.getString(resId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract String getId();
|
|
||||||
|
|
||||||
abstract boolean isEnabledByDefault();
|
|
||||||
|
|
||||||
abstract String getSpoken(WorkoutRecorder recorder);
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
*
|
|
||||||
* This file is part of FitoTrack
|
|
||||||
*
|
|
||||||
* FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.tadris.fitness.recording.announcement;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import de.tadris.fitness.R;
|
|
||||||
import de.tadris.fitness.recording.WorkoutRecorder;
|
|
||||||
import de.tadris.fitness.util.unit.UnitUtils;
|
|
||||||
|
|
||||||
public class AnnouncementAverageSpeed extends Announcement {
|
|
||||||
|
|
||||||
public AnnouncementAverageSpeed(Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return "avgSpeed";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean isEnabledByDefault() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String getSpoken(WorkoutRecorder recorder) {
|
|
||||||
String avgSpeed = UnitUtils.getSpeed(recorder.getAvgSpeed());
|
|
||||||
return getString(R.string.workoutAvgSpeedLong) + ": " + avgSpeed + ".";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
*
|
|
||||||
* This file is part of FitoTrack
|
|
||||||
*
|
|
||||||
* FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.tadris.fitness.recording.announcement;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import de.tadris.fitness.R;
|
|
||||||
import de.tadris.fitness.recording.WorkoutRecorder;
|
|
||||||
import de.tadris.fitness.util.unit.UnitUtils;
|
|
||||||
|
|
||||||
public class AnnouncementDistance extends Announcement {
|
|
||||||
|
|
||||||
public AnnouncementDistance(Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return "distance";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean isEnabledByDefault() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String getSpoken(WorkoutRecorder recorder) {
|
|
||||||
final String distance = UnitUtils.getDistance(recorder.getDistanceInMeters());
|
|
||||||
return getString(R.string.workoutDistance) + ": " + distance + ".";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
*
|
|
||||||
* This file is part of FitoTrack
|
|
||||||
*
|
|
||||||
* FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.tadris.fitness.recording.announcement;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import de.tadris.fitness.R;
|
|
||||||
import de.tadris.fitness.recording.WorkoutRecorder;
|
|
||||||
|
|
||||||
public class AnnouncementDuration extends Announcement {
|
|
||||||
|
|
||||||
public AnnouncementDuration(Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return "duration";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean isEnabledByDefault() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String getSpoken(WorkoutRecorder recorder) {
|
|
||||||
return getString(R.string.workoutDuration) + ": " + getSpokenTime(recorder.getDuration()) + ".";
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getSpokenTime(long duration) {
|
|
||||||
final long minute = 1000L * 60;
|
|
||||||
final long hour = minute * 60;
|
|
||||||
|
|
||||||
StringBuilder spokenTime = new StringBuilder();
|
|
||||||
|
|
||||||
if (duration > hour) {
|
|
||||||
long hours = duration / hour;
|
|
||||||
duration = duration % hour; // Set duration to the rest
|
|
||||||
spokenTime.append(hours).append(" ");
|
|
||||||
spokenTime.append(getString(hours == 1 ? R.string.timeHourSingular : R.string.timeHourPlural)).append(" ")
|
|
||||||
.append(getString(R.string.and)).append(" ");
|
|
||||||
}
|
|
||||||
long minutes = duration / minute;
|
|
||||||
spokenTime.append(minutes).append(" ");
|
|
||||||
spokenTime.append(getString(minutes == 1 ? R.string.timeMinuteSingular : R.string.timeMinutePlural));
|
|
||||||
|
|
||||||
return spokenTime.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
*
|
|
||||||
* This file is part of FitoTrack
|
|
||||||
*
|
|
||||||
* FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.tadris.fitness.recording.announcement;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class AnnouncementManager {
|
|
||||||
|
|
||||||
private Context context;
|
|
||||||
private List<Announcement> announcements = new ArrayList<>();
|
|
||||||
|
|
||||||
public AnnouncementManager(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
addAnnouncements();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addAnnouncements() {
|
|
||||||
announcements.add(new AnnouncementGPSStatus(context));
|
|
||||||
announcements.add(new AnnouncementDuration(context));
|
|
||||||
announcements.add(new AnnouncementDistance(context));
|
|
||||||
announcements.add(new AnnouncementAverageSpeed(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Announcement> getAnnouncements() {
|
|
||||||
return announcements;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,160 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
*
|
|
||||||
* This file is part of FitoTrack
|
|
||||||
*
|
|
||||||
* FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.tadris.fitness.recording.announcement;
|
|
||||||
|
|
||||||
import android.bluetooth.BluetoothAdapter;
|
|
||||||
import android.bluetooth.BluetoothHeadset;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.media.AudioManager;
|
|
||||||
import android.speech.tts.TextToSpeech;
|
|
||||||
import android.speech.tts.UtteranceProgressListener;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import de.tadris.fitness.Instance;
|
|
||||||
import de.tadris.fitness.data.UserPreferences;
|
|
||||||
import de.tadris.fitness.recording.WorkoutRecorder;
|
|
||||||
import de.tadris.fitness.util.unit.UnitUtils;
|
|
||||||
|
|
||||||
public class VoiceAnnouncements {
|
|
||||||
|
|
||||||
private TextToSpeech textToSpeech;
|
|
||||||
private boolean ttsAvailable;
|
|
||||||
private VoiceAnnouncementCallback callback;
|
|
||||||
private final AnnouncementManager manager;
|
|
||||||
|
|
||||||
private long lastSpokenUpdateTime = 0;
|
|
||||||
private int lastSpokenUpdateDistance = 0;
|
|
||||||
|
|
||||||
private final AnnouncementMode currentMode;
|
|
||||||
private final long intervalTime;
|
|
||||||
private final int intervalInMeters;
|
|
||||||
|
|
||||||
private final AudioManager audioManager;
|
|
||||||
|
|
||||||
public VoiceAnnouncements(Context context, VoiceAnnouncementCallback callback) {
|
|
||||||
this.callback = callback;
|
|
||||||
UserPreferences prefs = Instance.getInstance(context).userPreferences;
|
|
||||||
textToSpeech = new TextToSpeech(context, this::ttsReady);
|
|
||||||
|
|
||||||
this.intervalTime = 60 * 1000 * prefs.getSpokenUpdateTimePeriod();
|
|
||||||
this.intervalInMeters = (int) (1000.0 / UnitUtils.CHOSEN_SYSTEM.getDistanceFromKilometers(1) * prefs.getSpokenUpdateDistancePeriod());
|
|
||||||
|
|
||||||
this.manager = new AnnouncementManager(context);
|
|
||||||
this.audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
|
||||||
|
|
||||||
this.currentMode = AnnouncementMode.getCurrentMode(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ttsReady(int status) {
|
|
||||||
ttsAvailable = status == TextToSpeech.SUCCESS && textToSpeech.setLanguage(Locale.getDefault()) >= 0;
|
|
||||||
if (ttsAvailable) {
|
|
||||||
textToSpeech.setOnUtteranceProgressListener(new TextToSpeechListener());
|
|
||||||
}
|
|
||||||
callback.onVoiceAnnouncementIsReady(ttsAvailable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void check(WorkoutRecorder recorder) {
|
|
||||||
if (!ttsAvailable) {
|
|
||||||
return;
|
|
||||||
} // Cannot speak
|
|
||||||
|
|
||||||
boolean shouldSpeak = false;
|
|
||||||
|
|
||||||
if (intervalTime != 0 && recorder.getDuration() - lastSpokenUpdateTime > intervalTime) {
|
|
||||||
shouldSpeak = true;
|
|
||||||
}
|
|
||||||
if (intervalInMeters != 0 && recorder.getDistanceInMeters() - lastSpokenUpdateDistance > intervalInMeters) {
|
|
||||||
shouldSpeak = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldSpeak) {
|
|
||||||
speak(recorder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void speak(WorkoutRecorder recorder) {
|
|
||||||
for (Announcement announcement : manager.getAnnouncements()) {
|
|
||||||
speak(recorder, announcement);
|
|
||||||
}
|
|
||||||
|
|
||||||
lastSpokenUpdateTime = recorder.getDuration();
|
|
||||||
lastSpokenUpdateDistance = recorder.getDistanceInMeters();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void speak(WorkoutRecorder recorder, Announcement announcement) {
|
|
||||||
if (!announcement.isEnabled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String text = announcement.getSpoken(recorder);
|
|
||||||
if (!text.equals("")) {
|
|
||||||
speak(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int speakId = 1;
|
|
||||||
|
|
||||||
public void speak(String text) {
|
|
||||||
if (!ttsAvailable) {
|
|
||||||
// Cannot speak
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (currentMode == AnnouncementMode.HEADPHONES && !isHeadsetOn()) {
|
|
||||||
// Not allowed to speak
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Log.d("Recorder", "TTS speaks: " + text);
|
|
||||||
textToSpeech.speak(text, TextToSpeech.QUEUE_ADD, null, "announcement" + (++speakId));
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isHeadsetOn() {
|
|
||||||
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
|
||||||
boolean bluetoothHeadsetConnected = mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()
|
|
||||||
&& mBluetoothAdapter.getProfileConnectionState(BluetoothHeadset.HEADSET) == BluetoothHeadset.STATE_CONNECTED;
|
|
||||||
|
|
||||||
return audioManager.isWiredHeadsetOn() || bluetoothHeadsetConnected;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void destroy() {
|
|
||||||
textToSpeech.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TextToSpeechListener extends UtteranceProgressListener {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStart(String utteranceId) {
|
|
||||||
audioManager.requestAudioFocus(null, AudioManager.STREAM_SYSTEM, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDone(String utteranceId) {
|
|
||||||
audioManager.abandonAudioFocus(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(String utteranceId) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface VoiceAnnouncementCallback {
|
|
||||||
void onVoiceAnnouncementIsReady(boolean available);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,76 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
*
|
|
||||||
* This file is part of FitoTrack
|
|
||||||
*
|
|
||||||
* FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.tadris.fitness.util;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import de.tadris.fitness.R;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class has the task to correct the altitude.
|
|
||||||
* <p>
|
|
||||||
* In Germany or UK for example the GPS height differs
|
|
||||||
* roughly 50m from the real elevation over sea level.
|
|
||||||
* <p>
|
|
||||||
* The altitude given by GPS is the altitude over the WGS84 reference ellipsoid
|
|
||||||
* but we want the height over the sea level. That's why we have to correct the height.
|
|
||||||
* Luckily I found a file containing the corrections for all places around the world.
|
|
||||||
* <p>
|
|
||||||
* The geoids.csv is from https://github.com/vectorstofinal/geoid_heights licensed under MIT
|
|
||||||
*/
|
|
||||||
public class AltitudeCorrection {
|
|
||||||
|
|
||||||
private Context context;
|
|
||||||
private int latitude, longitude;
|
|
||||||
private double offset; // Basically how much higher the sea-level than the ellipsoid is
|
|
||||||
|
|
||||||
public AltitudeCorrection(Context context, int latitude, int longitude) throws IOException {
|
|
||||||
this.context = context;
|
|
||||||
this.latitude = latitude;
|
|
||||||
this.longitude = longitude;
|
|
||||||
findOffset();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void findOffset() throws IOException {
|
|
||||||
InputStream inputStream = context.getResources().openRawResource(R.raw.geoids);
|
|
||||||
List<String> list = IOUtils.readLines(inputStream, StandardCharsets.UTF_8);
|
|
||||||
for (String line : list) {
|
|
||||||
String[] data = line.split(",");
|
|
||||||
int lat = Integer.parseInt(data[0]);
|
|
||||||
int lon = Integer.parseInt(data[1]);
|
|
||||||
double offset = Double.parseDouble(data[2]);
|
|
||||||
if (lat == this.latitude && lon == this.longitude) {
|
|
||||||
this.offset = offset;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getHeightOverSeaLevel(double heightOverEllipsoid) {
|
|
||||||
return heightOverEllipsoid - offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -20,13 +20,12 @@
|
|||||||
package de.tadris.fitness.util;
|
package de.tadris.fitness.util;
|
||||||
|
|
||||||
import de.tadris.fitness.data.Workout;
|
import de.tadris.fitness.data.Workout;
|
||||||
import de.tadris.fitness.data.WorkoutType;
|
|
||||||
|
|
||||||
public class CalorieCalculator {
|
public class CalorieCalculator {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* workoutType, duration, ascent and avgSpeed of workout have to be set
|
* workoutType, duration and avgSpeed of workout have to be set
|
||||||
*
|
*
|
||||||
* @param workout the workout
|
* @param workout the workout
|
||||||
* @param weight the weight of the person in kilogram
|
* @param weight the weight of the person in kilogram
|
||||||
@ -44,18 +43,21 @@ public class CalorieCalculator {
|
|||||||
* workoutType and avgSpeed of workout have to be set
|
* workoutType and avgSpeed of workout have to be set
|
||||||
*
|
*
|
||||||
* Calculation currently ignores height.
|
* Calculation currently ignores height.
|
||||||
|
*
|
||||||
|
* @param workout
|
||||||
* @return MET
|
* @return MET
|
||||||
*/
|
*/
|
||||||
private static double getMET(Workout workout) {
|
public static double getMET(Workout workout){
|
||||||
double speedInKmh= workout.avgSpeed * 3.6;
|
double speedInKmh= workout.avgSpeed * 3.6;
|
||||||
WorkoutType type = workout.getWorkoutType();
|
if(workout.workoutType.equals(Workout.WORKOUT_TYPE_RUNNING) || workout.workoutType.equals(Workout.WORKOUT_TYPE_HIKING)){
|
||||||
if (type == WorkoutType.RUNNING || type == WorkoutType.HIKING) {
|
// This is a linear graph based on the website linked above
|
||||||
return Math.max(1.5, speedInKmh*1.117 - 2.1906);
|
return Math.max(1.5, speedInKmh*1.117 - 2.1906);
|
||||||
}
|
}
|
||||||
if (type == WorkoutType.CYCLING) {
|
if(workout.workoutType.equals(Workout.WORKOUT_TYPE_CYCLING)){
|
||||||
|
// This is a linear graph based on the website linked above
|
||||||
return Math.max(3, (speedInKmh-10) / 1.5);
|
return Math.max(3, (speedInKmh-10) / 1.5);
|
||||||
}
|
}
|
||||||
return 0;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,78 +0,0 @@
|
|||||||
package de.tadris.fitness.util;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Environment;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
import de.tadris.fitness.R;
|
|
||||||
|
|
||||||
public class FileUtils {
|
|
||||||
|
|
||||||
public static void saveOrShareFile(Activity activity, Uri uri, String suffix) {
|
|
||||||
String[] colors = {activity.getString(R.string.share), activity.getString(R.string.save)};
|
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
|
||||||
builder.setItems(colors, (dialog, which) -> {
|
|
||||||
if (which == 0) {
|
|
||||||
shareFile(activity, uri);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
saveFile(activity, uri, suffix);
|
|
||||||
Toast.makeText(activity, R.string.savedToDownloads, Toast.LENGTH_LONG).show();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Toast.makeText(activity, R.string.savingFailed, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
builder.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void saveFile(Activity activity, Uri fileUri, String suffix) throws IOException {
|
|
||||||
File target = new File(Environment.getExternalStorageDirectory(), "Download/fitotrack" + System.currentTimeMillis() + "." + suffix);
|
|
||||||
if (!target.createNewFile()) {
|
|
||||||
throw new IOException("Cannot write to file " + target);
|
|
||||||
}
|
|
||||||
copyFile(activity, fileUri, Uri.fromFile(target));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void copyFile(Activity activity, Uri sourceUri, Uri targetUri) throws IOException {
|
|
||||||
InputStream input = activity.getContentResolver().openInputStream(sourceUri);
|
|
||||||
if (input == null) {
|
|
||||||
throw new IOException("Source file not found");
|
|
||||||
}
|
|
||||||
OutputStream output = activity.getContentResolver().openOutputStream(targetUri);
|
|
||||||
IOUtils.copy(input, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void shareFile(Activity activity, Uri uri) {
|
|
||||||
Intent intentShareFile = new Intent(Intent.ACTION_SEND);
|
|
||||||
intentShareFile.setDataAndType(uri, activity.getContentResolver().getType(uri));
|
|
||||||
intentShareFile.putExtra(Intent.EXTRA_STREAM, uri);
|
|
||||||
intentShareFile.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
||||||
|
|
||||||
activity.startActivity(Intent.createChooser(intentShareFile, activity.getString(R.string.shareFile)));
|
|
||||||
|
|
||||||
Log.d("Export", uri.toString());
|
|
||||||
Log.d("Export", activity.getContentResolver().getType(uri));
|
|
||||||
try {
|
|
||||||
Log.d("Export", new BufferedInputStream(activity.getContentResolver().openInputStream(uri)).toString());
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,75 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
*
|
|
||||||
* This file is part of FitoTrack
|
|
||||||
*
|
|
||||||
* FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.tadris.fitness.util;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
|
|
||||||
import androidx.annotation.StyleRes;
|
|
||||||
|
|
||||||
import de.tadris.fitness.R;
|
|
||||||
import de.tadris.fitness.data.WorkoutType;
|
|
||||||
|
|
||||||
public class FitoTrackThemes {
|
|
||||||
|
|
||||||
private static final int THEME_SETTING_LIGHT = 0;
|
|
||||||
private static final int THEME_SETTING_DARK = 1;
|
|
||||||
|
|
||||||
private Context context;
|
|
||||||
|
|
||||||
public FitoTrackThemes(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@StyleRes
|
|
||||||
public int getDefaultTheme() {
|
|
||||||
if (shouldUseLightMode()) {
|
|
||||||
return R.style.AppTheme;
|
|
||||||
} else {
|
|
||||||
return R.style.AppThemeDark;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@StyleRes
|
|
||||||
public int getWorkoutTypeTheme(WorkoutType type) {
|
|
||||||
if (shouldUseLightMode()) {
|
|
||||||
return type.lightTheme;
|
|
||||||
} else {
|
|
||||||
return type.darkTheme;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean shouldUseLightMode() {
|
|
||||||
switch (getThemeSetting()) {
|
|
||||||
default:
|
|
||||||
case THEME_SETTING_LIGHT:
|
|
||||||
return true;
|
|
||||||
case THEME_SETTING_DARK:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getThemeSetting() {
|
|
||||||
String setting = PreferenceManager.getDefaultSharedPreferences(context).getString("themeSetting", String.valueOf(THEME_SETTING_LIGHT));
|
|
||||||
assert setting != null;
|
|
||||||
return Integer.parseInt(setting);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -28,7 +28,7 @@ import de.tadris.fitness.R;
|
|||||||
|
|
||||||
public class NotificationHelper {
|
public class NotificationHelper {
|
||||||
|
|
||||||
public static final String CHANNEL_WORKOUT = "workout";
|
public static String CHANNEL_WORKOUT= "workout";
|
||||||
|
|
||||||
public static void createChannels(Context context){
|
public static void createChannels(Context context){
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -17,39 +17,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/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.tadris.fitness.recording.announcement;
|
package de.tadris.fitness.util;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import de.tadris.fitness.R;
|
import de.tadris.fitness.R;
|
||||||
import de.tadris.fitness.recording.WorkoutRecorder;
|
import de.tadris.fitness.data.Workout;
|
||||||
|
|
||||||
public class AnnouncementGPSStatus extends Announcement {
|
public class ThemeManager {
|
||||||
|
|
||||||
public AnnouncementGPSStatus(Context context) {
|
public static int getThemeByWorkoutType(String type){
|
||||||
super(context);
|
switch (type){
|
||||||
|
case Workout.WORKOUT_TYPE_RUNNING: return R.style.Running;
|
||||||
|
case Workout.WORKOUT_TYPE_CYCLING: return R.style.Bicycling;
|
||||||
|
case Workout.WORKOUT_TYPE_HIKING: return R.style.Hiking;
|
||||||
|
default: return R.style.AppTheme;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static int getThemeByWorkout(Workout workout){
|
||||||
public String getId() {
|
return getThemeByWorkoutType(workout.workoutType);
|
||||||
return "gps-lost";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean isEnabledByDefault() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String getSpoken(WorkoutRecorder recorder) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSpokenGPSLost() {
|
|
||||||
return getString(R.string.gpsLost);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSpokenGPSFound() {
|
|
||||||
return getString(R.string.gpsFound);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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.util;
|
||||||
|
|
||||||
|
import de.tadris.fitness.R;
|
||||||
|
import de.tadris.fitness.data.Workout;
|
||||||
|
|
||||||
|
public class WorkoutTypeCalculator {
|
||||||
|
|
||||||
|
public static int getType(Workout workout){
|
||||||
|
if(workout.workoutType.equals(Workout.WORKOUT_TYPE_RUNNING)){
|
||||||
|
if(workout.avgSpeed < 1.9){
|
||||||
|
return R.string.workoutTypeWalking;
|
||||||
|
}else if(workout.avgSpeed < 2.7){
|
||||||
|
return R.string.workoutTypeJogging;
|
||||||
|
}else{
|
||||||
|
return R.string.workoutTypeRunning;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(workout.workoutType.equals(Workout.WORKOUT_TYPE_CYCLING)){
|
||||||
|
return R.string.workoutTypeCycling;
|
||||||
|
}
|
||||||
|
if(workout.workoutType.equals(Workout.WORKOUT_TYPE_HIKING)){
|
||||||
|
return R.string.workoutTypeHiking;
|
||||||
|
}
|
||||||
|
return R.string.workoutTypeUnknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
132
app/src/main/java/de/tadris/fitness/util/export/Exporter.java
Normal file
132
app/src/main/java/de/tadris/fitness/util/export/Exporter.java
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
|
*
|
||||||
|
* This file is part of FitoTrack
|
||||||
|
*
|
||||||
|
* FitoTrack is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* FitoTrack is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.tadris.fitness.util.export;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import de.tadris.fitness.Instance;
|
||||||
|
import de.tadris.fitness.R;
|
||||||
|
import de.tadris.fitness.data.AppDatabase;
|
||||||
|
import de.tadris.fitness.data.UserPreferences;
|
||||||
|
import de.tadris.fitness.data.Workout;
|
||||||
|
import de.tadris.fitness.data.WorkoutSample;
|
||||||
|
import de.tadris.fitness.util.unit.UnitUtils;
|
||||||
|
|
||||||
|
public class Exporter {
|
||||||
|
|
||||||
|
public static final int VERSION= 1;
|
||||||
|
|
||||||
|
public static void exportData(Context context, File output, ExportStatusListener listener) throws IOException {
|
||||||
|
listener.onStatusChanged(0, context.getString(R.string.initialising));
|
||||||
|
UserPreferences preferences= Instance.getInstance(context).userPreferences;
|
||||||
|
AppDatabase database= Instance.getInstance(context).db;
|
||||||
|
UnitUtils.setUnit(context);
|
||||||
|
|
||||||
|
FitoTrackDataContainer container= new FitoTrackDataContainer();
|
||||||
|
container.version= VERSION;
|
||||||
|
container.workouts= new ArrayList<>();
|
||||||
|
container.samples= new ArrayList<>();
|
||||||
|
|
||||||
|
listener.onStatusChanged(10, context.getString(R.string.preferences));
|
||||||
|
FitoTrackSettings settings= new FitoTrackSettings();
|
||||||
|
settings.weight= preferences.getUserWeight();
|
||||||
|
settings.mapStyle= preferences.getMapStyle();
|
||||||
|
settings.preferredUnitSystem= String.valueOf(UnitUtils.CHOSEN_SYSTEM.getId());
|
||||||
|
container.settings= settings;
|
||||||
|
|
||||||
|
listener.onStatusChanged(20, context.getString(R.string.workouts));
|
||||||
|
container.workouts.addAll(Arrays.asList(database.workoutDao().getWorkouts()));
|
||||||
|
listener.onStatusChanged(40, context.getString(R.string.locationData));
|
||||||
|
container.samples.addAll(Arrays.asList(database.workoutDao().getSamples()));
|
||||||
|
|
||||||
|
listener.onStatusChanged(60, context.getString(R.string.converting));
|
||||||
|
|
||||||
|
XmlMapper mapper= new XmlMapper();
|
||||||
|
mapper.writeValue(output, container);
|
||||||
|
|
||||||
|
listener.onStatusChanged(100, context.getString(R.string.finished));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ApplySharedPref")
|
||||||
|
public static void importData(Context context, Uri input, ExportStatusListener listener) throws IOException, UnsupportedVersionException {
|
||||||
|
listener.onStatusChanged(0, context.getString(R.string.loadingFile));
|
||||||
|
XmlMapper xmlMapper = new XmlMapper();
|
||||||
|
xmlMapper.configure(JsonParser.Feature.IGNORE_UNDEFINED, true);
|
||||||
|
FitoTrackDataContainer container = xmlMapper.readValue(context.getContentResolver().openInputStream(input), FitoTrackDataContainer.class);
|
||||||
|
|
||||||
|
if(container.version != 1){
|
||||||
|
throw new UnsupportedVersionException("Version Code" + container.version + " is unsupported!");
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.onStatusChanged(40, context.getString(R.string.preferences));
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
.edit().clear()
|
||||||
|
.putInt("weight", container.settings.weight)
|
||||||
|
.putString("unitSystem", container.settings.preferredUnitSystem)
|
||||||
|
.putBoolean("firstStart", false).putString("mapStyle", container.settings.mapStyle)
|
||||||
|
.commit();
|
||||||
|
|
||||||
|
AppDatabase database= Instance.getInstance(context).db;
|
||||||
|
|
||||||
|
database.runInTransaction(() -> {
|
||||||
|
database.clearAllTables();
|
||||||
|
|
||||||
|
listener.onStatusChanged(60, context.getString(R.string.workouts));
|
||||||
|
if(container.workouts != null){
|
||||||
|
for(Workout workout : container.workouts){
|
||||||
|
database.workoutDao().insertWorkout(workout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.onStatusChanged(80, context.getString(R.string.locationData));
|
||||||
|
if(container.samples != null){
|
||||||
|
for(WorkoutSample sample : container.samples){
|
||||||
|
database.workoutDao().insertSample(sample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
listener.onStatusChanged(100, context.getString(R.string.finished));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface ExportStatusListener{
|
||||||
|
void onStatusChanged(int progress, String action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class UnsupportedVersionException extends Exception{
|
||||||
|
public UnsupportedVersionException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -17,7 +17,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.tadris.fitness.export;
|
package de.tadris.fitness.util.export;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
|
||||||
@ -29,18 +29,20 @@ import de.tadris.fitness.data.WorkoutSample;
|
|||||||
|
|
||||||
@JacksonXmlRootElement(localName = "fito-track")
|
@JacksonXmlRootElement(localName = "fito-track")
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
class FitoTrackDataContainer {
|
public class FitoTrackDataContainer {
|
||||||
|
|
||||||
private int version;
|
int version;
|
||||||
private List<Workout> workouts;
|
List<Workout> workouts;
|
||||||
private List<WorkoutSample> samples;
|
List<WorkoutSample> samples;
|
||||||
|
FitoTrackSettings settings;
|
||||||
|
|
||||||
public FitoTrackDataContainer(){}
|
public FitoTrackDataContainer(){}
|
||||||
|
|
||||||
public FitoTrackDataContainer(int version, List<Workout> workouts, List<WorkoutSample> samples) {
|
public FitoTrackDataContainer(int version, List<Workout> workouts, List<WorkoutSample> samples, FitoTrackSettings settings) {
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.workouts = workouts;
|
this.workouts = workouts;
|
||||||
this.samples = samples;
|
this.samples = samples;
|
||||||
|
this.settings = settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getVersion() {
|
public int getVersion() {
|
||||||
@ -67,5 +69,11 @@ class FitoTrackDataContainer {
|
|||||||
this.samples = samples;
|
this.samples = samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FitoTrackSettings getSettings() {
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSettings(FitoTrackSettings settings) {
|
||||||
|
this.settings = settings;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
public class FitoTrackSettings {
|
||||||
|
|
||||||
|
String preferredUnitSystem;
|
||||||
|
int weight;
|
||||||
|
String mapStyle;
|
||||||
|
|
||||||
|
public FitoTrackSettings(){}
|
||||||
|
|
||||||
|
public FitoTrackSettings(String preferredUnitSystem, int weight, String mapStyle) {
|
||||||
|
this.preferredUnitSystem = preferredUnitSystem;
|
||||||
|
this.weight = weight;
|
||||||
|
this.mapStyle = mapStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPreferredUnitSystem() {
|
||||||
|
return preferredUnitSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPreferredUnitSystem(String preferredUnitSystem) {
|
||||||
|
this.preferredUnitSystem = preferredUnitSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWeight() {
|
||||||
|
return weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWeight(int weight) {
|
||||||
|
this.weight = weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMapStyle() {
|
||||||
|
return mapStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMapStyle(String mapStyle) {
|
||||||
|
this.mapStyle = mapStyle;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -37,7 +37,7 @@ public class Gpx {
|
|||||||
Metadata metadata;
|
Metadata metadata;
|
||||||
|
|
||||||
String name;
|
String name;
|
||||||
private String desc;
|
String desc;
|
||||||
|
|
||||||
@JacksonXmlElementWrapper(useWrapping = false)
|
@JacksonXmlElementWrapper(useWrapping = false)
|
||||||
List<Track> trk;
|
List<Track> trk;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
package de.tadris.fitness.util.gpx;
|
package de.tadris.fitness.util.gpx;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||||
@ -41,7 +40,7 @@ public class GpxExporter {
|
|||||||
mapper.writeValue(file, getGpxFromWorkout(context, workout));
|
mapper.writeValue(file, getGpxFromWorkout(context, workout));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Gpx getGpxFromWorkout(Context context, Workout workout) {
|
public static Gpx getGpxFromWorkout(Context context, Workout workout){
|
||||||
Gpx gpx= new Gpx();
|
Gpx gpx= new Gpx();
|
||||||
gpx.name= workout.toString();
|
gpx.name= workout.toString();
|
||||||
gpx.version= "1.1";
|
gpx.version= "1.1";
|
||||||
@ -53,7 +52,7 @@ public class GpxExporter {
|
|||||||
return gpx;
|
return gpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Track getTrackFromWorkout(Context context, Workout workout, int number) {
|
public static Track getTrackFromWorkout(Context context, Workout workout, int number){
|
||||||
WorkoutSample[] samples= Instance.getInstance(context).db.workoutDao().getAllSamplesOfWorkout(workout.id);
|
WorkoutSample[] samples= Instance.getInstance(context).db.workoutDao().getAllSamplesOfWorkout(workout.id);
|
||||||
Track track= new Track();
|
Track track= new Track();
|
||||||
track.number= number;
|
track.number= number;
|
||||||
@ -61,7 +60,7 @@ public class GpxExporter {
|
|||||||
track.cmt= workout.comment;
|
track.cmt= workout.comment;
|
||||||
track.desc= workout.comment;
|
track.desc= workout.comment;
|
||||||
track.src= "FitoTrack";
|
track.src= "FitoTrack";
|
||||||
track.type = workout.getWorkoutType().id;
|
track.type= workout.workoutType;
|
||||||
track.trkseg= new ArrayList<>();
|
track.trkseg= new ArrayList<>();
|
||||||
|
|
||||||
TrackSegment segment= new TrackSegment();
|
TrackSegment segment= new TrackSegment();
|
||||||
@ -78,14 +77,13 @@ public class GpxExporter {
|
|||||||
return track;
|
return track;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("SimpleDateFormat")
|
|
||||||
private static final SimpleDateFormat formatter= new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
|
private static final SimpleDateFormat formatter= new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
|
||||||
|
|
||||||
private static String getDateTime(long time) {
|
public static String getDateTime(long time){
|
||||||
return getDateTime(new Date(time));
|
return getDateTime(new Date(time));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getDateTime(Date date) {
|
public static String getDateTime(Date date){
|
||||||
return formatter.format(date);
|
return formatter.format(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -21,9 +21,9 @@ package de.tadris.fitness.util.gpx;
|
|||||||
|
|
||||||
public class Metadata {
|
public class Metadata {
|
||||||
|
|
||||||
private String name;
|
String name;
|
||||||
private String desc;
|
String desc;
|
||||||
private String time;
|
String time;
|
||||||
|
|
||||||
public Metadata() {
|
public Metadata() {
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -21,23 +21,21 @@ package de.tadris.fitness.util.gpx;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||||
|
|
||||||
class TrackPoint {
|
public class TrackPoint {
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true)
|
@JacksonXmlProperty(isAttribute = true)
|
||||||
private
|
|
||||||
double lat;
|
double lat;
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true)
|
@JacksonXmlProperty(isAttribute = true)
|
||||||
private
|
|
||||||
double lon;
|
double lon;
|
||||||
|
|
||||||
private double ele;
|
double ele;
|
||||||
|
|
||||||
private String time;
|
String time;
|
||||||
|
|
||||||
private String fix;
|
String fix;
|
||||||
|
|
||||||
private TrackPointExtension extensions;
|
TrackPointExtension extensions;
|
||||||
|
|
||||||
public TrackPoint(){}
|
public TrackPoint(){}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -21,7 +21,7 @@ package de.tadris.fitness.util.gpx;
|
|||||||
|
|
||||||
public class TrackPointExtension {
|
public class TrackPointExtension {
|
||||||
|
|
||||||
private double speed;
|
double speed;
|
||||||
|
|
||||||
public TrackPointExtension(){}
|
public TrackPointExtension(){}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -23,7 +23,7 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
class TrackSegment {
|
public class TrackSegment {
|
||||||
|
|
||||||
@JacksonXmlElementWrapper(useWrapping = false)
|
@JacksonXmlElementWrapper(useWrapping = false)
|
||||||
List<TrackPoint> trkpt;
|
List<TrackPoint> trkpt;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -24,23 +24,22 @@ import android.preference.PreferenceManager;
|
|||||||
|
|
||||||
public class UnitUtils {
|
public class UnitUtils {
|
||||||
|
|
||||||
private static final Unit UNITS_METRIC = new Metric();
|
public static final Unit UNITS_METRIC= new Metric();
|
||||||
private static final Unit UNITS_METRIC_PHYSICAL = new MetricPhysical();
|
public static final Unit UNITS_METRIC_PHYSICAL= new MetricPhysical();
|
||||||
private static final Unit UNITS_IMPERIAL_YARDS = new Imperial();
|
public static final Unit UNITS_IMPERIAL_YARDS= new Imperial();
|
||||||
private static final Unit UNITS_IMPERIAL_METERS = new ImperialWithMeters();
|
public static final Unit UNITS_IMPERIAL_METERS= new ImperialWithMeters();
|
||||||
private static final Unit[] supportedUnits = new Unit[]{
|
public static final Unit[] supportedUnits= new Unit[] {
|
||||||
UNITS_METRIC, UNITS_METRIC_PHYSICAL, UNITS_IMPERIAL_YARDS, UNITS_IMPERIAL_METERS
|
UNITS_METRIC, UNITS_METRIC_PHYSICAL, UNITS_IMPERIAL_YARDS, UNITS_IMPERIAL_METERS
|
||||||
};
|
};
|
||||||
|
|
||||||
public static Unit CHOSEN_SYSTEM= UNITS_METRIC;
|
public static Unit CHOSEN_SYSTEM= UNITS_METRIC;
|
||||||
|
|
||||||
public static void setUnit(Context context){
|
public static void setUnit(Context context){
|
||||||
String id = PreferenceManager.getDefaultSharedPreferences(context).getString("unitSystem", String.valueOf(UnitUtils.UNITS_METRIC.getId()));
|
int id= Integer.parseInt(PreferenceManager.getDefaultSharedPreferences(context).getString("unitSystem", String.valueOf(UnitUtils.UNITS_METRIC.getId())));
|
||||||
assert id != null;
|
setUnit(id);
|
||||||
setUnit(Integer.parseInt(id));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setUnit(int id) {
|
public static void setUnit(int id){
|
||||||
CHOSEN_SYSTEM= UNITS_METRIC;
|
CHOSEN_SYSTEM= UNITS_METRIC;
|
||||||
for(Unit unit : supportedUnits){
|
for(Unit unit : supportedUnits){
|
||||||
if(id == unit.getId()){
|
if(id == unit.getId()){
|
||||||
@ -63,38 +62,47 @@ public class UnitUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String getHourMinuteSecondTime(long time){
|
public static String getHourMinuteSecondTime(long time){
|
||||||
long totalSecs = time / 1000;
|
long totalSeks= time / 1000;
|
||||||
long totalMins = totalSecs / 60;
|
long totalMins= totalSeks / 60;
|
||||||
long hours= totalMins / 60;
|
long hours= totalMins / 60;
|
||||||
long mins= totalMins % 60;
|
long mins= totalMins % 60;
|
||||||
long secs = totalSecs % 60;
|
long seks= totalSeks % 60;
|
||||||
String minStr= (mins < 10 ? "0" : "") + mins;
|
String minStr= (mins < 10 ? "0" : "") + mins;
|
||||||
String sekStr = (secs < 10 ? "0" : "") + secs;
|
String sekStr= (seks < 10 ? "0" : "") + seks;
|
||||||
return hours + ":" + minStr + ":" + sekStr;
|
return hours + ":" + minStr + ":" + sekStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getPace(double metricPace) {
|
/**
|
||||||
|
*
|
||||||
|
* @param pace Pace in min/km
|
||||||
|
* @return Pace
|
||||||
|
*/
|
||||||
|
public static String getPace(double pace){
|
||||||
double one= CHOSEN_SYSTEM.getDistanceFromKilometers(1);
|
double one= CHOSEN_SYSTEM.getDistanceFromKilometers(1);
|
||||||
double secondsTotal = 60 * metricPace / one;
|
return round(pace / one, 1) + " min/" + CHOSEN_SYSTEM.getLongDistanceUnit();
|
||||||
int minutes = (int) secondsTotal / 60;
|
|
||||||
int seconds = (int) secondsTotal % 60;
|
|
||||||
return minutes + ":" + (seconds < 10 ? "0" : "") + seconds + " min/" + CHOSEN_SYSTEM.getLongDistanceUnit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*CHOSEN_SYSTEM.getLongDistanceUnit()
|
*CHOSEN_SYSTEM.getLongDistanceUnit()
|
||||||
* @param consumption consumption in kcal/km
|
* @param consumption consumption in kcal/km
|
||||||
|
* @return
|
||||||
*/
|
*/
|
||||||
public static String getRelativeEnergyConsumption(double consumption){
|
public static String getRelativeEnergyConsumption(double consumption){
|
||||||
double one= CHOSEN_SYSTEM.getDistanceFromKilometers(1);
|
double one= CHOSEN_SYSTEM.getDistanceFromKilometers(1);
|
||||||
return round(consumption / one, 2) + " kcal/" + CHOSEN_SYSTEM.getLongDistanceUnit();
|
return round(consumption / one, 2) + " kcal/" + CHOSEN_SYSTEM.getLongDistanceUnit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getDistance(int distanceInMeters) {
|
/**
|
||||||
if (distanceInMeters >= 1000) {
|
*
|
||||||
return round(CHOSEN_SYSTEM.getDistanceFromKilometers((double) distanceInMeters / 1000d), 1) + " " + CHOSEN_SYSTEM.getLongDistanceUnit();
|
* @param distance Distance in meters
|
||||||
|
* @return String in preferred unit
|
||||||
|
*/
|
||||||
|
public static String getDistance(int distance){
|
||||||
|
double units= CHOSEN_SYSTEM.getDistanceFromMeters(distance);
|
||||||
|
if(units >= 1000){
|
||||||
|
return round(units / 1000, 1) + " " + CHOSEN_SYSTEM.getLongDistanceUnit();
|
||||||
}else{
|
}else{
|
||||||
return (int) CHOSEN_SYSTEM.getDistanceFromMeters(distanceInMeters) + " " + CHOSEN_SYSTEM.getShortDistanceUnit();
|
return (int)units + " " + CHOSEN_SYSTEM.getShortDistanceUnit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +115,7 @@ public class UnitUtils {
|
|||||||
return round(CHOSEN_SYSTEM.getSpeedFromMeterPerSecond(speed), 1) + " " + CHOSEN_SYSTEM.getSpeedUnit();
|
return round(CHOSEN_SYSTEM.getSpeedFromMeterPerSecond(speed), 1) + " " + CHOSEN_SYSTEM.getSpeedUnit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double round(double d, int count) {
|
public static double round(double d, int count){
|
||||||
return (double)Math.round(d * Math.pow(10, count)) / Math.pow(10, count);
|
return (double)Math.round(d * Math.pow(10, count)) / Math.pow(10, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -28,8 +28,8 @@ import de.tadris.fitness.R;
|
|||||||
|
|
||||||
public class ProgressDialogController {
|
public class ProgressDialogController {
|
||||||
|
|
||||||
private final Activity context;
|
private Activity context;
|
||||||
private final Dialog dialog;
|
private Dialog dialog;
|
||||||
private TextView infoView;
|
private TextView infoView;
|
||||||
private ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ public class ProgressDialogController {
|
|||||||
setTitle(title);
|
setTitle(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProgressDialogController(Activity context) {
|
public ProgressDialogController(Activity context) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.dialog= new Dialog(context);
|
this.dialog= new Dialog(context);
|
||||||
initDialog();
|
initDialog();
|
||||||
@ -51,7 +51,7 @@ public class ProgressDialogController {
|
|||||||
progressBar= dialog.findViewById(R.id.dialogProgressBar);
|
progressBar= dialog.findViewById(R.id.dialogProgressBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setTitle(String title) {
|
public void setTitle(String title){
|
||||||
dialog.setTitle(title);
|
dialog.setTitle(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
*
|
*
|
||||||
* This file is part of FitoTrack
|
* This file is part of FitoTrack
|
||||||
*
|
*
|
||||||
@ -32,6 +32,7 @@ import java.util.Date;
|
|||||||
|
|
||||||
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.WorkoutTypeCalculator;
|
||||||
import de.tadris.fitness.util.unit.UnitUtils;
|
import de.tadris.fitness.util.unit.UnitUtils;
|
||||||
|
|
||||||
public class WorkoutAdapter extends RecyclerView.Adapter<WorkoutAdapter.WorkoutViewHolder>{
|
public class WorkoutAdapter extends RecyclerView.Adapter<WorkoutAdapter.WorkoutViewHolder>{
|
||||||
@ -39,14 +40,10 @@ public class WorkoutAdapter extends RecyclerView.Adapter<WorkoutAdapter.WorkoutV
|
|||||||
|
|
||||||
public static class WorkoutViewHolder extends RecyclerView.ViewHolder{
|
public static class WorkoutViewHolder extends RecyclerView.ViewHolder{
|
||||||
|
|
||||||
final View root;
|
View root;
|
||||||
final TextView lengthText;
|
TextView lengthText, timeText, dateText, typeText, commentText;
|
||||||
final TextView timeText;
|
|
||||||
final TextView dateText;
|
|
||||||
final TextView typeText;
|
|
||||||
final TextView commentText;
|
|
||||||
|
|
||||||
WorkoutViewHolder(@NonNull View itemView) {
|
public WorkoutViewHolder(@NonNull View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
this.root= itemView;
|
this.root= itemView;
|
||||||
lengthText= itemView.findViewById(R.id.workoutLength);
|
lengthText= itemView.findViewById(R.id.workoutLength);
|
||||||
@ -57,8 +54,8 @@ public class WorkoutAdapter extends RecyclerView.Adapter<WorkoutAdapter.WorkoutV
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Workout[] workouts;
|
Workout[] workouts;
|
||||||
private final WorkoutAdapterListener listener;
|
WorkoutAdapterListener listener;
|
||||||
|
|
||||||
public WorkoutAdapter(Workout[] workouts, WorkoutAdapterListener listener) {
|
public WorkoutAdapter(Workout[] workouts, WorkoutAdapterListener listener) {
|
||||||
this.workouts = workouts;
|
this.workouts = workouts;
|
||||||
@ -77,7 +74,7 @@ public class WorkoutAdapter extends RecyclerView.Adapter<WorkoutAdapter.WorkoutV
|
|||||||
public void onBindViewHolder(WorkoutViewHolder holder, final int position) {
|
public void onBindViewHolder(WorkoutViewHolder holder, final int position) {
|
||||||
Workout workout= workouts[position];
|
Workout workout= workouts[position];
|
||||||
holder.dateText.setText(SimpleDateFormat.getDateTimeInstance().format(new Date(workout.start)));
|
holder.dateText.setText(SimpleDateFormat.getDateTimeInstance().format(new Date(workout.start)));
|
||||||
holder.typeText.setText(workout.getWorkoutType().title);
|
holder.typeText.setText(WorkoutTypeCalculator.getType(workout));
|
||||||
if(workout.comment != null){
|
if(workout.comment != null){
|
||||||
if(workout.comment.length() > 33){
|
if(workout.comment.length() > 33){
|
||||||
holder.commentText.setText(workout.comment.substring(0, 30) + "...");
|
holder.commentText.setText(workout.comment.substring(0, 30) + "...");
|
||||||
@ -89,7 +86,7 @@ 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(position, workout));
|
holder.root.setOnClickListener(v -> listener.onItemClick(workout));
|
||||||
holder.root.setOnLongClickListener(v -> {
|
holder.root.setOnLongClickListener(v -> {
|
||||||
listener.onItemLongClick(position, workout);
|
listener.onItemLongClick(position, workout);
|
||||||
return true;
|
return true;
|
||||||
@ -103,7 +100,7 @@ public class WorkoutAdapter extends RecyclerView.Adapter<WorkoutAdapter.WorkoutV
|
|||||||
}
|
}
|
||||||
|
|
||||||
public interface WorkoutAdapterListener{
|
public interface WorkoutAdapterListener{
|
||||||
void onItemClick(int pos, Workout workout);
|
void onItemClick(Workout workout);
|
||||||
void onItemLongClick(int pos, Workout workout);
|
void onItemLongClick(int pos, Workout workout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
<!--
|
||||||
|
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
~
|
~
|
||||||
~ This file is part of FitoTrack
|
~ This file is part of FitoTrack
|
||||||
~
|
~
|
||||||
@ -19,7 +20,5 @@
|
|||||||
|
|
||||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:duration="300">
|
android:duration="300">
|
||||||
<alpha
|
<alpha android:fromAlpha="0" android:toAlpha="1"/>
|
||||||
android:fromAlpha="0"
|
|
||||||
android:toAlpha="1" />
|
|
||||||
</set>
|
</set>
|
||||||
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
<!--
|
||||||
|
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
~
|
~
|
||||||
~ This file is part of FitoTrack
|
~ This file is part of FitoTrack
|
||||||
~
|
~
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
~
|
~
|
||||||
~ This file is part of FitoTrack
|
~ This file is part of FitoTrack
|
||||||
~
|
~
|
||||||
@ -20,10 +20,10 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:tint="#FFFFFF"
|
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24"
|
||||||
|
android:tint="#FFFFFF">
|
||||||
<path
|
<path
|
||||||
android:fillColor="#FF000000"
|
android:fillColor="#FF000000"
|
||||||
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
|
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
~
|
~
|
||||||
~ This file is part of FitoTrack
|
~ This file is part of FitoTrack
|
||||||
~
|
~
|
||||||
@ -20,11 +20,11 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:alpha="0.8"
|
|
||||||
android:tint="#FFFFFF"
|
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24"
|
||||||
|
android:tint="#FFFFFF"
|
||||||
|
android:alpha="0.8">
|
||||||
<path
|
<path
|
||||||
android:fillColor="#FF000000"
|
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" />
|
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>
|
</vector>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
~
|
~
|
||||||
~ This file is part of FitoTrack
|
~ This file is part of FitoTrack
|
||||||
~
|
~
|
||||||
@ -18,11 +18,11 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@android:color/white"
|
android:pathData="M12,12m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"
|
||||||
android:pathData="M12,12m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0" />
|
android:fillColor="@android:color/white"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
@ -1,32 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
~
|
|
||||||
~ This file is part of FitoTrack
|
|
||||||
~
|
|
||||||
~ FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 3 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:context=".activity.EnterWorkoutActivity">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/enterWorkoutRoot"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="15dp" />
|
|
||||||
</ScrollView>
|
|
||||||
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
<!--
|
||||||
|
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
~
|
~
|
||||||
~ This file is part of FitoTrack
|
~ This file is part of FitoTrack
|
||||||
~
|
~
|
||||||
@ -18,10 +19,10 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout 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"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
tools:context=".activity.ListWorkoutsActivity">
|
tools:context=".activity.ListWorkoutsActivity">
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
@ -35,12 +36,12 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="bottom|right"
|
android:layout_gravity="bottom|right"
|
||||||
android:layout_margin="10dp"
|
android:layout_margin="10dp"
|
||||||
app:menu_animationDelayPerItem="50"
|
|
||||||
app:menu_colorNormal="@color/colorPrimary"
|
app:menu_colorNormal="@color/colorPrimary"
|
||||||
app:menu_colorPressed="@color/colorPrimaryDark"
|
app:menu_colorPressed="@color/colorPrimaryDark"
|
||||||
|
app:menu_animationDelayPerItem="50"
|
||||||
app:menu_icon="@drawable/fab_add"
|
app:menu_icon="@drawable/fab_add"
|
||||||
app:menu_labels_hideAnimation="@anim/fab_slide_out_to_right"
|
app:menu_labels_showAnimation="@anim/fab_slide_in_from_right"
|
||||||
app:menu_labels_showAnimation="@anim/fab_slide_in_from_right">
|
app:menu_labels_hideAnimation="@anim/fab_slide_out_to_right">
|
||||||
|
|
||||||
<com.github.clans.fab.FloatingActionButton
|
<com.github.clans.fab.FloatingActionButton
|
||||||
android:id="@+id/workoutListRecordRunning"
|
android:id="@+id/workoutListRecordRunning"
|
||||||
@ -50,7 +51,7 @@
|
|||||||
app:fab_colorNormal="@color/colorPrimaryRunning"
|
app:fab_colorNormal="@color/colorPrimaryRunning"
|
||||||
app:fab_colorPressed="@color/colorPrimaryDarkRunning"
|
app:fab_colorPressed="@color/colorPrimaryDarkRunning"
|
||||||
app:fab_label="@string/workoutTypeRunning"
|
app:fab_label="@string/workoutTypeRunning"
|
||||||
app:fab_size="normal" />
|
app:fab_size="normal"/>
|
||||||
|
|
||||||
<com.github.clans.fab.FloatingActionButton
|
<com.github.clans.fab.FloatingActionButton
|
||||||
android:id="@+id/workoutListRecordHiking"
|
android:id="@+id/workoutListRecordHiking"
|
||||||
@ -70,17 +71,7 @@
|
|||||||
app:fab_colorNormal="@color/colorPrimaryBicycling"
|
app:fab_colorNormal="@color/colorPrimaryBicycling"
|
||||||
app:fab_colorPressed="@color/colorPrimaryDarkBicycling"
|
app:fab_colorPressed="@color/colorPrimaryDarkBicycling"
|
||||||
app:fab_label="@string/workoutTypeCycling"
|
app:fab_label="@string/workoutTypeCycling"
|
||||||
app:fab_size="normal" />
|
app:fab_size="normal"/>
|
||||||
|
|
||||||
<com.github.clans.fab.FloatingActionButton
|
|
||||||
android:id="@+id/workoutListEnter"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:src="@drawable/ic_add_white"
|
|
||||||
app:fab_colorNormal="@color/colorPrimary"
|
|
||||||
app:fab_colorPressed="@color/colorPrimaryDark"
|
|
||||||
app:fab_label="@string/enterWorkout"
|
|
||||||
app:fab_size="normal" />
|
|
||||||
|
|
||||||
</com.github.clans.fab.FloatingActionMenu>
|
</com.github.clans.fab.FloatingActionMenu>
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
<!--
|
||||||
|
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
~
|
~
|
||||||
~ This file is part of FitoTrack
|
~ This file is part of FitoTrack
|
||||||
~
|
~
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
<!--
|
||||||
|
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
~
|
~
|
||||||
~ This file is part of FitoTrack
|
~ This file is part of FitoTrack
|
||||||
~
|
~
|
||||||
@ -31,7 +32,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/recordMapViewerRoot"
|
android:id="@+id/recordMapViewrRoot"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
@ -55,15 +56,6 @@
|
|||||||
android:text="@string/gps"
|
android:text="@string/gps"
|
||||||
android:textColor="@android:color/transparent" />
|
android:textColor="@android:color/transparent" />
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/recordMapAttribution"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="bottom|center"
|
|
||||||
android:layout_margin="5dp"
|
|
||||||
android:textColor="@color/textColorLight"
|
|
||||||
android:text="@string/OpenStreetMapAttribution" />
|
|
||||||
|
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
@ -73,8 +65,8 @@
|
|||||||
android:layout_height="200dp"
|
android:layout_height="200dp"
|
||||||
android:layout_alignParentBottom="true"
|
android:layout_alignParentBottom="true"
|
||||||
android:layout_marginBottom="0dp"
|
android:layout_marginBottom="0dp"
|
||||||
android:orientation="vertical"
|
android:padding="10dp"
|
||||||
android:padding="10dp">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/recordTime"
|
android:id="@+id/recordTime"
|
||||||
@ -83,7 +75,7 @@
|
|||||||
android:fontFamily="sans-serif-black"
|
android:fontFamily="sans-serif-black"
|
||||||
android:text="0:44:08"
|
android:text="0:44:08"
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
android:textColor="?android:textColorPrimary"
|
android:textColor="@android:color/black"
|
||||||
android:textSize="30sp"
|
android:textSize="30sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
@ -125,7 +117,7 @@
|
|||||||
android:text="2,06 km"
|
android:text="2,06 km"
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
android:textAllCaps="false"
|
android:textAllCaps="false"
|
||||||
android:textColor="?android:textColorPrimary"
|
android:textColor="@android:color/black"
|
||||||
android:textSize="24sp"
|
android:textSize="24sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
@ -145,7 +137,7 @@
|
|||||||
android:text="30 kcal"
|
android:text="30 kcal"
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
android:textAllCaps="false"
|
android:textAllCaps="false"
|
||||||
android:textColor="?android:textColorPrimary"
|
android:textColor="@android:color/black"
|
||||||
android:textSize="24sp"
|
android:textSize="24sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
@ -164,7 +156,7 @@
|
|||||||
android:id="@+id/recordInfo3Title"
|
android:id="@+id/recordInfo3Title"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/workoutAvgSpeedShort"
|
android:text="@string/workoutAvgSpeed"
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
android:textAllCaps="true"
|
android:textAllCaps="true"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
@ -176,7 +168,7 @@
|
|||||||
android:text="7 km/h"
|
android:text="7 km/h"
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
android:textAllCaps="false"
|
android:textAllCaps="false"
|
||||||
android:textColor="?android:textColorPrimary"
|
android:textColor="@android:color/black"
|
||||||
android:textSize="24sp"
|
android:textSize="24sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
@ -196,7 +188,7 @@
|
|||||||
android:text="30 kcal"
|
android:text="30 kcal"
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
android:textAllCaps="false"
|
android:textAllCaps="false"
|
||||||
android:textColor="?android:textColorPrimary"
|
android:textColor="@android:color/black"
|
||||||
android:textSize="24sp"
|
android:textSize="24sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
<!--
|
||||||
|
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
~
|
~
|
||||||
~ This file is part of FitoTrack
|
~ This file is part of FitoTrack
|
||||||
~
|
~
|
||||||
@ -34,7 +35,7 @@
|
|||||||
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" />
|
android:padding="20dp"></LinearLayout>
|
||||||
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
<!--
|
||||||
|
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
~
|
~
|
||||||
~ This file is part of FitoTrack
|
~ This file is part of FitoTrack
|
||||||
~
|
~
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
<!--
|
||||||
|
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
~
|
~
|
||||||
~ This file is part of FitoTrack
|
~ This file is part of FitoTrack
|
||||||
~
|
~
|
||||||
@ -24,7 +25,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
tools:context=".activity.ShowWorkoutMapActivity">
|
tools:context=".activity.ShowWorkoutMapActivity" >
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Guideline
|
<androidx.constraintlayout.widget.Guideline
|
||||||
android:id="@+id/guideline4"
|
android:id="@+id/guideline4"
|
||||||
@ -57,6 +58,6 @@
|
|||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="@+id/guideline4" />
|
app:layout_constraintTop_toTopOf="@+id/guideline4"></LinearLayout>
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
</android.support.constraint.ConstraintLayout>
|
||||||
@ -1,54 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
~
|
|
||||||
~ This file is part of FitoTrack
|
|
||||||
~
|
|
||||||
~ FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 3 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="fill_parent"
|
|
||||||
android:gravity="center"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<NumberPicker
|
|
||||||
android:id="@+id/durationPickerHours"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="30dp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text=":" />
|
|
||||||
|
|
||||||
<NumberPicker
|
|
||||||
android:id="@+id/durationPickerMinutes"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="30dp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text=":" />
|
|
||||||
|
|
||||||
<NumberPicker
|
|
||||||
android:id="@+id/durationPickerSeconds"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="30dp" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
<!--
|
||||||
|
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
~
|
~
|
||||||
~ This file is part of FitoTrack
|
~ This file is part of FitoTrack
|
||||||
~
|
~
|
||||||
|
|||||||
@ -1,40 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
~ Copyright (c) 2020 Ruslan Sokolovski <russok@gmail.com>
|
|
||||||
~
|
|
||||||
~ 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/>.
|
|
||||||
-->
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="fill_parent" >
|
|
||||||
|
|
||||||
<NumberPicker
|
|
||||||
android:id="@+id/spokenUpdatesTimePicker"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentTop="true"
|
|
||||||
android:layout_margin="50dp"
|
|
||||||
android:layout_alignParentLeft="true"/>
|
|
||||||
|
|
||||||
<NumberPicker
|
|
||||||
android:id="@+id/spokenUpdatesDistancePicker"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentTop="true"
|
|
||||||
android:layout_margin="50dp"
|
|
||||||
android:layout_alignParentRight="true"/>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
<!--
|
||||||
|
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
~
|
~
|
||||||
~ This file is part of FitoTrack
|
~ This file is part of FitoTrack
|
||||||
~
|
~
|
||||||
@ -28,12 +29,11 @@
|
|||||||
|
|
||||||
<TableRow
|
<TableRow
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent" >
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:labelFor="@id/uploadDescription"
|
|
||||||
android:text="@string/description" />
|
android:text="@string/description" />
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
@ -48,7 +48,7 @@
|
|||||||
|
|
||||||
<TableRow
|
<TableRow
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent" >
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@ -64,6 +64,13 @@
|
|||||||
android:spinnerMode="dropdown" />
|
android:spinnerMode="dropdown" />
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
</TableLayout>
|
</TableLayout>
|
||||||
|
|
||||||
<CheckBox
|
<CheckBox
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
<!--
|
||||||
|
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
~
|
~
|
||||||
~ This file is part of FitoTrack
|
~ This file is part of FitoTrack
|
||||||
~
|
~
|
||||||
@ -19,7 +20,7 @@
|
|||||||
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="fill_parent">
|
android:layout_height="fill_parent" >
|
||||||
|
|
||||||
<NumberPicker
|
<NumberPicker
|
||||||
android:id="@+id/weightPicker"
|
android:id="@+id/weightPicker"
|
||||||
|
|||||||
@ -1,71 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
~
|
|
||||||
~ This file is part of FitoTrack
|
|
||||||
~
|
|
||||||
~ FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 3 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_marginLeft="-15dp"
|
|
||||||
android:layout_marginRight="-15dp"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:background="?android:selectableItemBackground">
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="50dp"
|
|
||||||
android:paddingStart="15dp"
|
|
||||||
android:paddingEnd="15dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/lineKey"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentLeft="true"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:textAppearance="@android:style/TextAppearance.Material.Medium" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/lineViewRoot"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_toStartOf="@+id/lineValue"
|
|
||||||
android:layout_toEndOf="@+id/lineKey"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:gravity="right|center_vertical"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/lineValue"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentRight="true"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:textAlignment="textEnd"
|
|
||||||
android:textAppearance="@android:style/TextAppearance.Material.Medium" />
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="1dp"
|
|
||||||
android:background="?android:dividerHorizontal" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
~
|
|
||||||
~ This file is part of FitoTrack
|
|
||||||
~
|
|
||||||
~ FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 3 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:id="@android:id/text1"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:drawableStart="?android:attr/listChoiceIndicatorSingle"
|
|
||||||
android:drawablePadding="20dp"
|
|
||||||
android:ellipsize="marquee"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:minHeight="48dip"
|
|
||||||
android:paddingStart="20dp"
|
|
||||||
android:paddingEnd="24dp"
|
|
||||||
android:textAppearance="@android:style/TextAppearance.Material.Medium"
|
|
||||||
android:textColor="?android:attr/textColorAlertDialogListItem" />
|
|
||||||
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
<!--
|
||||||
|
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
~
|
~
|
||||||
~ This file is part of FitoTrack
|
~ This file is part of FitoTrack
|
||||||
~
|
~
|
||||||
@ -41,7 +42,7 @@
|
|||||||
android:fontFamily="sans-serif-black"
|
android:fontFamily="sans-serif-black"
|
||||||
android:text=""
|
android:text=""
|
||||||
android:textAllCaps="true"
|
android:textAllCaps="true"
|
||||||
android:textColor="?android:textColorSecondary"
|
android:textColor="@color/textColorLight"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -51,7 +52,7 @@
|
|||||||
android:fontFamily="sans-serif-black"
|
android:fontFamily="sans-serif-black"
|
||||||
android:text=""
|
android:text=""
|
||||||
android:textAllCaps="false"
|
android:textAllCaps="false"
|
||||||
android:textColor="?android:textColorPrimary"
|
android:textColor="@color/textLighterBlack"
|
||||||
android:textSize="24sp"
|
android:textSize="24sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@ -73,7 +74,7 @@
|
|||||||
android:fontFamily="sans-serif-black"
|
android:fontFamily="sans-serif-black"
|
||||||
android:text=""
|
android:text=""
|
||||||
android:textAllCaps="true"
|
android:textAllCaps="true"
|
||||||
android:textColor="?android:textColorSecondary"
|
android:textColor="@color/textColorLight"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -83,7 +84,7 @@
|
|||||||
android:fontFamily="sans-serif-black"
|
android:fontFamily="sans-serif-black"
|
||||||
android:text=""
|
android:text=""
|
||||||
android:textAllCaps="false"
|
android:textAllCaps="false"
|
||||||
android:textColor="?android:textColorPrimary"
|
android:textColor="@color/textLighterBlack"
|
||||||
android:textSize="24sp"
|
android:textSize="24sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
<!--
|
||||||
|
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
~
|
~
|
||||||
~ This file is part of FitoTrack
|
~ This file is part of FitoTrack
|
||||||
~
|
~
|
||||||
@ -20,8 +21,8 @@
|
|||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?android:selectableItemBackground"
|
android:orientation="vertical"
|
||||||
android:orientation="vertical">
|
android:background="?android:selectableItemBackground">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -36,7 +37,7 @@
|
|||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:text="Aug 19, 2019 20:15"
|
android:text="Aug 19, 2019 20:15"
|
||||||
android:textAllCaps="true"
|
android:textAllCaps="true"
|
||||||
android:textColor="?android:textColorPrimary"
|
android:textColor="@color/textLighterBlack"
|
||||||
android:textSize="18sp"
|
android:textSize="18sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
@ -80,7 +81,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="27min"
|
android:text="27min"
|
||||||
android:textColor="?android:textColorPrimary"
|
android:textColor="@color/textLighterBlack"
|
||||||
android:textSize="30sp" />
|
android:textSize="30sp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@ -89,6 +90,6 @@
|
|||||||
<View
|
<View
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
android:background="@android:color/darker_gray" />
|
android:background="@android:color/darker_gray"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@ -1,27 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
~
|
|
||||||
~ This file is part of FitoTrack
|
|
||||||
~
|
|
||||||
~ FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 3 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/actionEnterWorkoutAdd"
|
|
||||||
android:icon="@drawable/ic_add_white"
|
|
||||||
android:showAsAction="always"
|
|
||||||
android:title="@string/save" />
|
|
||||||
</menu>
|
|
||||||
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
<!--
|
||||||
|
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
~
|
~
|
||||||
~ This file is part of FitoTrack
|
~ This file is part of FitoTrack
|
||||||
~
|
~
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
<!--
|
||||||
|
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
~
|
~
|
||||||
~ This file is part of FitoTrack
|
~ This file is part of FitoTrack
|
||||||
~
|
~
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
<!--
|
||||||
|
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
~
|
~
|
||||||
~ This file is part of FitoTrack
|
~ This file is part of FitoTrack
|
||||||
~
|
~
|
||||||
@ -19,9 +20,6 @@
|
|||||||
|
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/actionEditComment"
|
|
||||||
android:title="@string/action_edit_comment" />
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/actionExportGpx"
|
android:id="@+id/actionExportGpx"
|
||||||
android:title="@string/exportAsGpxFile" />
|
android:title="@string/exportAsGpxFile" />
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
<!--
|
||||||
|
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
~
|
~
|
||||||
~ This file is part of FitoTrack
|
~ This file is part of FitoTrack
|
||||||
~
|
~
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
<!--
|
||||||
|
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
~
|
~
|
||||||
~ This file is part of FitoTrack
|
~ This file is part of FitoTrack
|
||||||
~
|
~
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,27 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
|
||||||
~
|
|
||||||
~ This file is part of FitoTrack
|
|
||||||
~
|
|
||||||
~ FitoTrack is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 3 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ FitoTrack is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<resources>
|
|
||||||
|
|
||||||
<string-array name="pref_announcement_mode">
|
|
||||||
<item>Immer</item>
|
|
||||||
<item>Nur mit Kopfhörern</item>
|
|
||||||
</string-array>
|
|
||||||
|
|
||||||
</resources>
|
|
||||||
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
<!--
|
||||||
|
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||||
~
|
~
|
||||||
~ This file is part of FitoTrack
|
~ This file is part of FitoTrack
|
||||||
~
|
~
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user