mirror of
https://github.com/russok/FitoTrack.git
synced 2025-10-29 00:32:11 -07:00
Compare commits
92 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be3ce4f1c3 | ||
|
|
d372e5ffff | ||
|
|
428256294a | ||
|
|
e69f40553a | ||
|
|
0d84407165 | ||
|
|
72650fefb4 | ||
|
|
815cd764c1 | ||
|
|
e0c3a860b2 | ||
|
|
32724a58f2 | ||
|
|
6cf233a5ea | ||
|
|
5adc2a0536 | ||
|
|
4a89e95b41 | ||
|
|
16c42ec350 | ||
|
|
3f7d590ded | ||
|
|
289e25949f | ||
|
|
8665d5ca14 | ||
|
|
e6ab3ab871 | ||
|
|
4a27bdba26 | ||
|
|
62570fed27 | ||
|
|
a336015818 | ||
|
|
3d930cc49e | ||
|
|
fe181b5fe4 | ||
|
|
3a22c97018 | ||
|
|
bf6c5d0923 | ||
|
|
a26d6a748b | ||
|
|
1ed129778b | ||
|
|
e978e8f6c2 | ||
|
|
e5f3f9f0f7 | ||
|
|
91d97ae805 | ||
|
|
5f84e57bf3 | ||
|
|
71033df2c0 | ||
|
|
18d04a53d3 | ||
|
|
fbaab9cb1b | ||
|
|
daf3e3fb31 | ||
|
|
af05b04acb | ||
|
|
1787e30dfa | ||
|
|
cd15b57768 | ||
|
|
ee657aff06 | ||
|
|
d628a43a84 | ||
|
|
a110964006 | ||
|
|
cde4eb5940 | ||
|
|
e397bc502b | ||
|
|
c83ffd8b4c | ||
|
|
112ea2d9f6 | ||
|
|
782be62f2b | ||
|
|
15c16ab09d | ||
|
|
2d776a3864 | ||
|
|
d75e84d8d9 | ||
|
|
de95affa50 | ||
|
|
f71c75f180 | ||
|
|
a36fc3ae71 | ||
|
|
1bd4be8356 | ||
|
|
6f803f62e4 | ||
|
|
20d0b859a2 | ||
|
|
fb2f78b9ee | ||
|
|
f1a8470a1d | ||
|
|
3123e3badb | ||
|
|
d6dc49ae84 | ||
|
|
4efa078169 | ||
|
|
baf529f9ca | ||
|
|
9089689ce3 | ||
|
|
832f6441cb | ||
|
|
59c1781dd4 | ||
|
|
3455702375 | ||
|
|
f0684647c3 | ||
|
|
dfdb2dbac3 | ||
|
|
5e5203848d | ||
|
|
ce06613b28 | ||
|
|
31528743d3 | ||
|
|
a3a4a5c05c | ||
|
|
5bfad8242e | ||
|
|
acbe5abb05 | ||
|
|
4e354a8078 | ||
|
|
819eb937db | ||
|
|
6b224bf264 | ||
|
|
2b1a0057aa | ||
|
|
a9ce6b2fdb | ||
|
|
2df035ef78 | ||
|
|
7da68b6fc7 | ||
|
|
ac3dfe4a7f | ||
|
|
3a0c38fdb4 | ||
|
|
f5d2093402 | ||
|
|
f5f2be0461 | ||
|
|
787e4333f0 | ||
|
|
83c0168108 | ||
|
|
7617809897 | ||
|
|
b9e16bda25 | ||
|
|
a0a1f44ef9 | ||
|
|
7b15571e1b | ||
|
|
db0690a351 | ||
|
|
96be601641 | ||
|
|
07b362ddcc |
18
.gitignore
vendored
Normal file
18
.gitignore
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
/app/build
|
||||
/app/release
|
||||
|
||||
# misc
|
||||
.*
|
||||
!.gitignore
|
||||
|
||||
# idea
|
||||
*.iml
|
||||
|
||||
# gradle
|
||||
local.properties
|
||||
|
||||
24
NOTICE.md
24
NOTICE.md
@ -49,6 +49,30 @@
|
||||
See the License for the specific language governing permissions and
|
||||
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>
|
||||
|
||||
© 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.
|
||||
- **Open-Source.** There is neither advertivesment nor tracking, and the source code is open and licensed under the GPLv3.
|
||||
|
||||
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.
|
||||
Please see the [Userguide](https://codeberg.org/jannis/FitoTrack/wiki/How-to-use) if you have any problems.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -35,8 +35,8 @@ android {
|
||||
applicationId "de.tadris.fitness"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 28
|
||||
versionCode 300
|
||||
versionName "3.0"
|
||||
versionCode 500
|
||||
versionName "5.0"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
buildTypes {
|
||||
@ -49,6 +49,12 @@ android {
|
||||
sourceCompatibility = '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 {
|
||||
@ -67,7 +73,7 @@ dependencies {
|
||||
implementation 'org.mapsforge:mapsforge-map-android:0.11.0'
|
||||
implementation 'com.caverock:androidsvg:1.3'
|
||||
|
||||
// Charts
|
||||
// UI
|
||||
implementation 'net.sf.kxml:kxml2:2.3.0'
|
||||
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
|
||||
implementation 'com.github.clans:fab:1.6.4'
|
||||
@ -76,8 +82,11 @@ dependencies {
|
||||
implementation 'stax:stax-api:1.0.1'
|
||||
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.8'
|
||||
|
||||
// File Utils
|
||||
implementation 'commons-io:commons-io:2.6'
|
||||
|
||||
// Upload to OSM
|
||||
implementation ('de.westnordost:osmapi-traces:1.0')
|
||||
implementation('de.westnordost:osmapi-traces:1.0')
|
||||
configurations {
|
||||
compile.exclude group: 'net.sf.kxml', module: 'kxml2' // already included in Android
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
@ -23,32 +23,38 @@
|
||||
package="de.tadris.fitness">
|
||||
|
||||
<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.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:appCategory="productivity"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme"
|
||||
android:appCategory="productivity"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
<activity android:name=".activity.ShowWorkoutMapActivity"
|
||||
android:screenOrientation="portrait"></activity>
|
||||
<activity android:name=".activity.ShowWorkoutMapDiagramActivity"
|
||||
android:screenOrientation="portrait"></activity>
|
||||
<activity android:name=".activity.VoiceAnnouncementsSettingsActivity" />
|
||||
<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
|
||||
android:name=".activity.ShowWorkoutActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name=".activity.RecordWorkoutActivity"
|
||||
android:showOnLockScreen="true"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name=".activity.ListWorkoutsActivity"
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -32,11 +32,12 @@ import java.util.List;
|
||||
import de.tadris.fitness.data.AppDatabase;
|
||||
import de.tadris.fitness.data.UserPreferences;
|
||||
import de.tadris.fitness.recording.LocationListener;
|
||||
import de.tadris.fitness.util.FitoTrackThemes;
|
||||
import de.tadris.fitness.util.unit.UnitUtils;
|
||||
|
||||
public class Instance {
|
||||
|
||||
public static final String DATABASE_NAME= "fito-track";
|
||||
private static final String DATABASE_NAME = "fito-track";
|
||||
|
||||
private static Instance instance;
|
||||
|
||||
@ -47,20 +48,22 @@ public class Instance {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public AppDatabase db;
|
||||
public List<LocationListener.LocationChangeListener> locationChangeListeners= new ArrayList<>();
|
||||
public UserPreferences userPreferences;
|
||||
public final AppDatabase db;
|
||||
public final List<LocationListener.LocationChangeListener> locationChangeListeners = new ArrayList<>();
|
||||
public final UserPreferences userPreferences;
|
||||
public final FitoTrackThemes themes;
|
||||
|
||||
public boolean pressureAvailable= false;
|
||||
public float lastPressure= 0;
|
||||
|
||||
private Instance(Context context) {
|
||||
userPreferences= new UserPreferences(context);
|
||||
themes = new FitoTrackThemes(context);
|
||||
db = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, DATABASE_NAME)
|
||||
.addMigrations(new Migration(1, 2) {
|
||||
@Override
|
||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||
try{
|
||||
try {
|
||||
database.beginTransaction();
|
||||
|
||||
database.execSQL("ALTER table workout add descent REAL NOT NULL DEFAULT 0;");
|
||||
@ -85,7 +88,20 @@ public class Instance {
|
||||
database.execSQL("DROP TABLE workout_sample2");
|
||||
|
||||
database.setTransactionSuccessful();
|
||||
}finally {
|
||||
} finally {
|
||||
database.endTransaction();
|
||||
}
|
||||
}
|
||||
}, new Migration(2, 3) {
|
||||
@Override
|
||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||
try {
|
||||
database.beginTransaction();
|
||||
|
||||
database.execSQL("ALTER table workout add COLUMN edited INTEGER not null default 0");
|
||||
|
||||
database.setTransactionSuccessful();
|
||||
} finally {
|
||||
database.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
* FitoTrack is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* FitoTrack is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.tadris.fitness.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.InputType;
|
||||
import android.util.TypedValue;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
|
||||
import de.tadris.fitness.R;
|
||||
import de.tadris.fitness.data.WorkoutBuilder;
|
||||
import de.tadris.fitness.data.WorkoutType;
|
||||
import de.tadris.fitness.dialog.DatePickerFragment;
|
||||
import de.tadris.fitness.dialog.DurationPickerDialogFragment;
|
||||
import de.tadris.fitness.dialog.SelectWorkoutTypeDialog;
|
||||
import de.tadris.fitness.dialog.TimePickerFragment;
|
||||
import de.tadris.fitness.util.unit.UnitUtils;
|
||||
|
||||
public class EnterWorkoutActivity extends InformationActivity implements SelectWorkoutTypeDialog.WorkoutTypeSelectListener, DatePickerFragment.DatePickerCallback, TimePickerFragment.TimePickerCallback, DurationPickerDialogFragment.DurationPickListener {
|
||||
|
||||
WorkoutBuilder workoutBuilder = new WorkoutBuilder();
|
||||
TextView typeTextView, dateTextView, timeTextView, durationTextView;
|
||||
EditText distanceEditText, commentEditText;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_enter_workout);
|
||||
|
||||
initRoot();
|
||||
|
||||
addTitle(getString(R.string.info));
|
||||
setupActionBar();
|
||||
|
||||
KeyValueLine typeLine = addKeyValueLine(getString(R.string.type));
|
||||
typeTextView = typeLine.value;
|
||||
typeLine.lineRoot.setOnClickListener(v -> showTypeSelection());
|
||||
|
||||
distanceEditText = new EditText(this);
|
||||
distanceEditText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
|
||||
distanceEditText.setSingleLine(true);
|
||||
distanceEditText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
|
||||
distanceEditText.setOnEditorActionListener((v, actionId, event) -> {
|
||||
// If the User clicks on the finish button on the keyboard, continue by showing the date selection
|
||||
if (actionId == EditorInfo.IME_ACTION_SEARCH ||
|
||||
actionId == EditorInfo.IME_ACTION_DONE ||
|
||||
event != null &&
|
||||
event.getAction() == KeyEvent.ACTION_DOWN &&
|
||||
event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
|
||||
if (event == null || !event.isShiftPressed()) {
|
||||
showDateSelection();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
addKeyValueLine(getString(R.string.workoutDistance), distanceEditText, UnitUtils.CHOSEN_SYSTEM.getLongDistanceUnit());
|
||||
|
||||
|
||||
KeyValueLine dateLine = addKeyValueLine(getString(R.string.workoutDate));
|
||||
dateLine.lineRoot.setOnClickListener(v -> showDateSelection());
|
||||
dateTextView = dateLine.value;
|
||||
|
||||
KeyValueLine timeLine = addKeyValueLine(getString(R.string.workoutStartTime));
|
||||
timeLine.lineRoot.setOnClickListener(v -> showTimeSelection());
|
||||
timeTextView = timeLine.value;
|
||||
|
||||
KeyValueLine durationLine = addKeyValueLine(getString(R.string.workoutDuration));
|
||||
durationLine.lineRoot.setOnClickListener(v -> showDurationSelection());
|
||||
durationTextView = durationLine.value;
|
||||
|
||||
addTitle(getString(R.string.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) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -19,48 +19,35 @@
|
||||
|
||||
package de.tadris.fitness.activity;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
import de.tadris.fitness.Instance;
|
||||
import de.tadris.fitness.R;
|
||||
|
||||
abstract public class FitoTrackActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setTheme(Instance.getInstance(this).themes.getDefaultTheme());
|
||||
}
|
||||
|
||||
|
||||
protected int getThemePrimaryColor() {
|
||||
int getThemePrimaryColor() {
|
||||
final TypedValue value = new TypedValue ();
|
||||
getTheme().resolveAttribute (android.R.attr.colorPrimary, value, true);
|
||||
return value.data;
|
||||
}
|
||||
|
||||
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){
|
||||
void showErrorDialog(Exception e, @StringRes int title, @StringRes int message) {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(title)
|
||||
.setMessage(getString(message) + "\n\n" + e.getMessage())
|
||||
@ -68,5 +55,16 @@ abstract public class FitoTrackActivity extends Activity {
|
||||
.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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
* FitoTrack is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* FitoTrack is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.tadris.fitness.activity;
|
||||
|
||||
import android.graphics.Typeface;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import de.tadris.fitness.R;
|
||||
|
||||
public abstract class InformationActivity extends FitoTrackActivity {
|
||||
|
||||
ViewGroup root;
|
||||
|
||||
protected void addTitle(String title) {
|
||||
TextView textView = new TextView(this);
|
||||
textView.setText(title);
|
||||
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
|
||||
textView.setTextColor(getThemePrimaryColor());
|
||||
textView.setTypeface(Typeface.DEFAULT_BOLD);
|
||||
textView.setAllCaps(true);
|
||||
textView.setPadding(0, 20, 0, 0);
|
||||
|
||||
root.addView(textView);
|
||||
}
|
||||
|
||||
protected TextView addText(String text, boolean themeColor) {
|
||||
TextView textView = createTextView(text, themeColor);
|
||||
root.addView(textView);
|
||||
|
||||
return textView;
|
||||
}
|
||||
|
||||
protected TextView createTextView(String text) {
|
||||
return createTextView(text, false);
|
||||
}
|
||||
|
||||
protected TextView createTextView(String text, boolean themeColor) {
|
||||
TextView textView = new TextView(this);
|
||||
textView.setText(text);
|
||||
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
|
||||
if (themeColor) {
|
||||
textView.setTextColor(getThemePrimaryColor());
|
||||
} else {
|
||||
textView.setTextColor(getResources().getColor(R.color.textLighterBlack));
|
||||
}
|
||||
textView.setPadding(0, 20, 0, 0);
|
||||
|
||||
return textView;
|
||||
}
|
||||
|
||||
protected void addKeyValue(String key1, String value1) {
|
||||
addKeyValue(key1, value1, "", "");
|
||||
}
|
||||
|
||||
protected void addKeyValue(String key1, String value1, String key2, String value2) {
|
||||
View v = getLayoutInflater().inflate(R.layout.show_entry, root, false);
|
||||
|
||||
TextView title1 = v.findViewById(R.id.v1title);
|
||||
TextView title2 = v.findViewById(R.id.v2title);
|
||||
TextView v1 = v.findViewById(R.id.v1value);
|
||||
TextView v2 = v.findViewById(R.id.v2value);
|
||||
|
||||
title1.setText(key1);
|
||||
title2.setText(key2);
|
||||
v1.setText(value1);
|
||||
v2.setText(value2);
|
||||
|
||||
root.addView(v);
|
||||
}
|
||||
|
||||
protected KeyValueLine addKeyValueLine(String key) {
|
||||
return addKeyValueLine(key, "");
|
||||
}
|
||||
|
||||
protected KeyValueLine addKeyValueLine(String key, String value) {
|
||||
return addKeyValueLine(key, null, value);
|
||||
}
|
||||
|
||||
protected KeyValueLine addKeyValueLine(String key, @Nullable View view) {
|
||||
return addKeyValueLine(key, view, "");
|
||||
}
|
||||
|
||||
protected KeyValueLine addKeyValueLine(String key, @Nullable View view, String value) {
|
||||
View v = getLayoutInflater().inflate(R.layout.enter_workout_line, root, false);
|
||||
|
||||
TextView keyView = v.findViewById(R.id.lineKey);
|
||||
TextView valueView = v.findViewById(R.id.lineValue);
|
||||
LinearLayout customViewRoot = v.findViewById(R.id.lineViewRoot);
|
||||
|
||||
keyView.setText(key);
|
||||
valueView.setText(value);
|
||||
|
||||
if (view != null) {
|
||||
customViewRoot.addView(view);
|
||||
}
|
||||
|
||||
root.addView(v);
|
||||
return new KeyValueLine(v, keyView, valueView, view);
|
||||
}
|
||||
|
||||
public static class KeyValueLine {
|
||||
public View lineRoot;
|
||||
public TextView key;
|
||||
public TextView value;
|
||||
public View customView;
|
||||
|
||||
public KeyValueLine(View lineRoot, TextView key, TextView value, View customView) {
|
||||
this.lineRoot = lineRoot;
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
this.customView = customView;
|
||||
}
|
||||
}
|
||||
|
||||
abstract void initRoot();
|
||||
|
||||
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -42,12 +42,12 @@ public class LauncherActivity extends Activity {
|
||||
new Handler().postDelayed(this::init, 100);
|
||||
}
|
||||
|
||||
void init(){
|
||||
private void init() {
|
||||
Instance.getInstance(this);
|
||||
start();
|
||||
}
|
||||
|
||||
void start(){
|
||||
private void start() {
|
||||
startActivity(new Intent(this, ListWorkoutsActivity.class));
|
||||
finish();
|
||||
overridePendingTransition(R.anim.fade_in, R.anim.stay);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -19,7 +19,6 @@
|
||||
|
||||
package de.tadris.fitness.activity;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
@ -37,16 +36,17 @@ import com.github.clans.fab.FloatingActionMenu;
|
||||
import de.tadris.fitness.Instance;
|
||||
import de.tadris.fitness.R;
|
||||
import de.tadris.fitness.data.Workout;
|
||||
import de.tadris.fitness.data.WorkoutType;
|
||||
import de.tadris.fitness.util.DialogUtils;
|
||||
import de.tadris.fitness.view.WorkoutAdapter;
|
||||
|
||||
public class ListWorkoutsActivity extends Activity implements WorkoutAdapter.WorkoutAdapterListener {
|
||||
public class ListWorkoutsActivity extends FitoTrackActivity implements WorkoutAdapter.WorkoutAdapterListener {
|
||||
|
||||
private RecyclerView listView;
|
||||
private RecyclerView.Adapter adapter;
|
||||
private RecyclerView.LayoutManager layoutManager;
|
||||
private FloatingActionMenu menu;
|
||||
Workout[] workouts;
|
||||
private Workout[] workouts;
|
||||
|
||||
|
||||
@Override
|
||||
@ -63,23 +63,21 @@ public class ListWorkoutsActivity extends Activity implements WorkoutAdapter.Wor
|
||||
menu= findViewById(R.id.workoutListMenu);
|
||||
menu.setOnMenuButtonLongClickListener(v -> {
|
||||
if(workouts.length > 0){
|
||||
startRecording(workouts[0].workoutType);
|
||||
startRecording(workouts[0].getWorkoutType());
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
findViewById(R.id.workoutListRecordRunning).setOnClickListener(v -> startRecording(Workout.WORKOUT_TYPE_RUNNING));
|
||||
findViewById(R.id.workoutListRecordHiking) .setOnClickListener(v -> startRecording(Workout.WORKOUT_TYPE_HIKING));
|
||||
findViewById(R.id.workoutListRecordCycling).setOnClickListener(v -> startRecording(Workout.WORKOUT_TYPE_CYCLING));
|
||||
|
||||
loadData();
|
||||
findViewById(R.id.workoutListRecordRunning).setOnClickListener(v -> startRecording(WorkoutType.RUNNING));
|
||||
findViewById(R.id.workoutListRecordHiking).setOnClickListener(v -> startRecording(WorkoutType.HIKING));
|
||||
findViewById(R.id.workoutListRecordCycling).setOnClickListener(v -> startRecording(WorkoutType.CYCLING));
|
||||
findViewById(R.id.workoutListEnter).setOnClickListener(v -> startEnterWorkoutActivity());
|
||||
|
||||
checkFirstStart();
|
||||
|
||||
refreshAdapter();
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
private void checkFirstStart(){
|
||||
@ -95,7 +93,13 @@ public class ListWorkoutsActivity extends Activity implements WorkoutAdapter.Wor
|
||||
}
|
||||
}
|
||||
|
||||
public void startRecording(String activity){
|
||||
private void startEnterWorkoutActivity() {
|
||||
menu.close(true);
|
||||
final Intent intent = new Intent(this, EnterWorkoutActivity.class);
|
||||
new Handler().postDelayed(() -> startActivity(intent), 300);
|
||||
}
|
||||
|
||||
private void startRecording(WorkoutType activity) {
|
||||
menu.close(true);
|
||||
RecordWorkoutActivity.ACTIVITY= activity;
|
||||
final Intent intent= new Intent(this, RecordWorkoutActivity.class);
|
||||
@ -106,15 +110,11 @@ public class ListWorkoutsActivity extends Activity implements WorkoutAdapter.Wor
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
int count= workouts.length;
|
||||
loadData();
|
||||
if(count != workouts.length){
|
||||
refreshAdapter();
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(Workout workout) {
|
||||
public void onItemClick(int pos, Workout workout) {
|
||||
ShowWorkoutActivity.selectedWorkout= workout;
|
||||
startActivity(new Intent(this, ShowWorkoutActivity.class));
|
||||
}
|
||||
@ -123,11 +123,15 @@ public class ListWorkoutsActivity extends Activity implements WorkoutAdapter.Wor
|
||||
public void onItemLongClick(int pos, Workout workout) {
|
||||
DialogUtils.showDeleteWorkoutDialog(this, () -> {
|
||||
Instance.getInstance(ListWorkoutsActivity.this).db.workoutDao().deleteWorkout(workout);
|
||||
loadData();
|
||||
adapter.notifyItemRemoved(pos);
|
||||
refresh();
|
||||
});
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
loadData();
|
||||
refreshAdapter();
|
||||
}
|
||||
|
||||
private void loadData(){
|
||||
workouts= Instance.getInstance(this).db.workoutDao().getWorkouts();
|
||||
}
|
||||
@ -148,10 +152,9 @@ public class ListWorkoutsActivity extends Activity implements WorkoutAdapter.Wor
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
|
||||
switch (id){
|
||||
case R.id.actionOpenSettings:
|
||||
startActivity(new Intent(this, SettingsActivity.class));
|
||||
return true;
|
||||
if (id == R.id.actionOpenSettings) {
|
||||
startActivity(new Intent(this, SettingsActivity.class));
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -35,6 +35,7 @@ import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
@ -53,54 +54,62 @@ import java.util.List;
|
||||
|
||||
import de.tadris.fitness.Instance;
|
||||
import de.tadris.fitness.R;
|
||||
import de.tadris.fitness.data.Workout;
|
||||
import de.tadris.fitness.data.WorkoutType;
|
||||
import de.tadris.fitness.map.MapManager;
|
||||
import de.tadris.fitness.map.tilesource.TileSources;
|
||||
import de.tadris.fitness.recording.LocationListener;
|
||||
import de.tadris.fitness.recording.PressureService;
|
||||
import de.tadris.fitness.recording.WorkoutRecorder;
|
||||
import de.tadris.fitness.util.ThemeManager;
|
||||
import de.tadris.fitness.recording.announcement.AnnouncementGPSStatus;
|
||||
import de.tadris.fitness.recording.announcement.VoiceAnnouncements;
|
||||
import de.tadris.fitness.util.unit.UnitUtils;
|
||||
|
||||
public class RecordWorkoutActivity extends FitoTrackActivity implements LocationListener.LocationChangeListener, WorkoutRecorder.WorkoutRecorderListener {
|
||||
public class RecordWorkoutActivity extends FitoTrackActivity implements LocationListener.LocationChangeListener, WorkoutRecorder.WorkoutRecorderListener, VoiceAnnouncements.VoiceAnnouncementCallback {
|
||||
|
||||
public static String ACTIVITY= Workout.WORKOUT_TYPE_RUNNING;
|
||||
public static WorkoutType ACTIVITY = WorkoutType.OTHER;
|
||||
|
||||
MapView mapView;
|
||||
TileDownloadLayer downloadLayer;
|
||||
WorkoutRecorder recorder;
|
||||
Polyline polyline;
|
||||
List<LatLong> latLongList= new ArrayList<>();
|
||||
InfoViewHolder[] infoViews= new InfoViewHolder[4];
|
||||
TextView timeView, gpsStatusView;
|
||||
View waitingForGPSOverlay;
|
||||
boolean gpsFound= false;
|
||||
boolean isResumed= false;
|
||||
private Handler mHandler= new Handler();
|
||||
PowerManager.WakeLock wakeLock;
|
||||
Intent locationListener;
|
||||
Intent pressureService;
|
||||
private MapView mapView;
|
||||
private TileDownloadLayer downloadLayer;
|
||||
private WorkoutRecorder recorder;
|
||||
private Polyline polyline;
|
||||
private final List<LatLong> latLongList = new ArrayList<>();
|
||||
private final InfoViewHolder[] infoViews = new InfoViewHolder[4];
|
||||
private TextView timeView;
|
||||
private TextView gpsStatusView;
|
||||
private TextView attribution;
|
||||
private View waitingForGPSOverlay;
|
||||
private boolean gpsFound = false;
|
||||
private boolean isResumed = false;
|
||||
private final Handler mHandler = new Handler();
|
||||
private PowerManager.WakeLock wakeLock;
|
||||
private Intent locationListener;
|
||||
private Intent pressureService;
|
||||
private boolean saved= false;
|
||||
|
||||
private VoiceAnnouncements voiceAnnouncements;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setTheme(ThemeManager.getThemeByWorkoutType(ACTIVITY));
|
||||
setTheme(Instance.getInstance(this).themes.getWorkoutTypeTheme(ACTIVITY));
|
||||
setContentView(R.layout.activity_record_workout);
|
||||
|
||||
setTitle(R.string.recordWorkout);
|
||||
|
||||
setupMap();
|
||||
|
||||
((ViewGroup)findViewById(R.id.recordMapViewrRoot)).addView(mapView);
|
||||
((ViewGroup) findViewById(R.id.recordMapViewerRoot)).addView(mapView);
|
||||
waitingForGPSOverlay= findViewById(R.id.recorderWaitingOverlay);
|
||||
waitingForGPSOverlay.setVisibility(View.VISIBLE);
|
||||
|
||||
attribution = findViewById(R.id.recordMapAttribution);
|
||||
|
||||
checkPermissions();
|
||||
|
||||
recorder= new WorkoutRecorder(this, ACTIVITY, this);
|
||||
recorder.start();
|
||||
|
||||
voiceAnnouncements = new VoiceAnnouncements(this, this);
|
||||
|
||||
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[2]= new InfoViewHolder(findViewById(R.id.recordInfo3Title), findViewById(R.id.recordInfo3Value));
|
||||
@ -129,7 +138,9 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
||||
waitingForGPSOverlay.clearAnimation();
|
||||
waitingForGPSOverlay.animate().alpha(0f).setDuration(1000).setListener(new Animator.AnimatorListener() {
|
||||
@Override public void onAnimationStart(Animator animator) { }
|
||||
|
||||
@Override public void onAnimationCancel(Animator animator) { }
|
||||
|
||||
@Override public void onAnimationRepeat(Animator animator) { }
|
||||
|
||||
@Override
|
||||
@ -137,17 +148,16 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
||||
waitingForGPSOverlay.setVisibility(View.GONE);
|
||||
}
|
||||
}).start();
|
||||
hideOSMAttribution();
|
||||
}
|
||||
|
||||
private void hideOSMAttribution() {
|
||||
attribution.animate().alpha(0f).setDuration(1000).setStartDelay(5000).start();
|
||||
}
|
||||
|
||||
private void setupMap(){
|
||||
this.mapView= new MapView(this);
|
||||
TileSources.Purpose purpose;
|
||||
if(ACTIVITY.equals(Workout.WORKOUT_TYPE_CYCLING)){
|
||||
purpose= TileSources.Purpose.CYCLING;
|
||||
}else{
|
||||
purpose= TileSources.Purpose.OUTDOOR;
|
||||
}
|
||||
downloadLayer= MapManager.setupMap(mapView, purpose);
|
||||
downloadLayer = MapManager.setupMap(mapView);
|
||||
}
|
||||
|
||||
private void updateLine(){
|
||||
@ -168,9 +178,7 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
||||
try{
|
||||
while (recorder.isActive()){
|
||||
Thread.sleep(1000);
|
||||
if(isResumed){
|
||||
mHandler.post(this::updateDescription);
|
||||
}
|
||||
mHandler.post(this::updateDescription);
|
||||
}
|
||||
}catch (InterruptedException e){
|
||||
e.printStackTrace();
|
||||
@ -178,15 +186,23 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
||||
}).start();
|
||||
}
|
||||
|
||||
int i= 0;
|
||||
|
||||
private void updateDescription(){
|
||||
i++;
|
||||
timeView.setText(UnitUtils.getHourMinuteSecondTime(recorder.getDuration()));
|
||||
infoViews[0].setText(getString(R.string.workoutDistance), UnitUtils.getDistance(recorder.getDistance()));
|
||||
infoViews[1].setText(getString(R.string.workoutBurnedEnergy), recorder.getCalories() + " kcal");
|
||||
infoViews[2].setText(getString(R.string.workoutAvgSpeed), UnitUtils.getSpeed(Math.min(100d, recorder.getAvgSpeed())));
|
||||
infoViews[3].setText(getString(R.string.workoutPauseDuration), UnitUtils.getHourMinuteSecondTime(recorder.getPauseDuration()));
|
||||
private void updateDescription() {
|
||||
long duration = recorder.getDuration();
|
||||
int distanceInMeters = recorder.getDistanceInMeters();
|
||||
final String distanceCaption = getString(R.string.workoutDistance);
|
||||
final String distance = UnitUtils.getDistance(distanceInMeters);
|
||||
final String avgSpeed = UnitUtils.getSpeed(Math.min(100d, recorder.getAvgSpeed()));
|
||||
if (isResumed) {
|
||||
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(){
|
||||
@ -235,14 +251,14 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
||||
.create().show();
|
||||
}
|
||||
|
||||
void checkPermissions(){
|
||||
private void checkPermissions() {
|
||||
if (!hasPermission()) {
|
||||
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 10);
|
||||
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 10);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasPermission(){
|
||||
private boolean hasPermission() {
|
||||
return ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|
||||
|| ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
@ -253,12 +269,12 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
||||
}
|
||||
}
|
||||
|
||||
public void stopListener(){
|
||||
private void stopListener() {
|
||||
stopService(locationListener);
|
||||
stopService(pressureService);
|
||||
}
|
||||
|
||||
public void startListener(){
|
||||
private void startListener() {
|
||||
if(locationListener == null){
|
||||
locationListener= new Intent(this, LocationListener.class);
|
||||
pressureService= new Intent(this, PressureService.class);
|
||||
@ -304,9 +320,15 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
recorder.stop();
|
||||
saveIfNotSaved(); // Important
|
||||
saveIfNotSaved(); // Important to save
|
||||
|
||||
// Clear map
|
||||
mapView.destroyAll();
|
||||
AndroidGraphicFactory.clearResourceMemoryCache();
|
||||
|
||||
// Shutdown TTS
|
||||
voiceAnnouncements.destroy();
|
||||
|
||||
super.onDestroy();
|
||||
if(wakeLock.isHeld()){
|
||||
wakeLock.release();
|
||||
@ -324,10 +346,18 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
||||
|
||||
public void onResume(){
|
||||
super.onResume();
|
||||
enableLockScreenVisibility();
|
||||
downloadLayer.onResume();
|
||||
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
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
// Inflate the menu; this adds items to the action bar if it is present.
|
||||
@ -362,13 +392,27 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
||||
gpsFound= true;
|
||||
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());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static class InfoViewHolder{
|
||||
TextView titleView, valueView;
|
||||
@Override
|
||||
public void onVoiceAnnouncementIsReady(boolean available) {
|
||||
}
|
||||
|
||||
public InfoViewHolder(TextView titleView, TextView valueView) {
|
||||
static class InfoViewHolder {
|
||||
final TextView titleView;
|
||||
final TextView valueView;
|
||||
|
||||
InfoViewHolder(TextView titleView, TextView valueView) {
|
||||
this.titleView = titleView;
|
||||
this.valueView = valueView;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -25,127 +25,31 @@ import android.app.AlertDialog;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.media.Ringtone;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
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.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.NumberPicker;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
|
||||
import de.tadris.fitness.R;
|
||||
import de.tadris.fitness.util.export.Exporter;
|
||||
import de.tadris.fitness.export.BackupController;
|
||||
import de.tadris.fitness.export.RestoreController;
|
||||
import de.tadris.fitness.recording.announcement.VoiceAnnouncements;
|
||||
import de.tadris.fitness.util.FileUtils;
|
||||
import de.tadris.fitness.util.unit.UnitUtils;
|
||||
import de.tadris.fitness.view.ProgressDialogController;
|
||||
|
||||
public class SettingsActivity extends PreferenceActivity {
|
||||
public class SettingsActivity extends FitoTrackSettingsActivity {
|
||||
|
||||
|
||||
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();
|
||||
private final Handler mHandler = new Handler();
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@ -158,22 +62,62 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
|
||||
bindPreferenceSummaryToValue(findPreference("unitSystem"));
|
||||
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 -> showWeightPicker());
|
||||
findPreference("import").setOnPreferenceClickListener(preference -> showImportDialog());
|
||||
findPreference("export").setOnPreferenceClickListener(preference -> showExportDialog());
|
||||
findPreference("weight").setOnPreferenceClickListener(preference -> {
|
||||
showWeightPicker();
|
||||
return true;
|
||||
});
|
||||
findPreference("speech").setOnPreferenceClickListener(preference -> {
|
||||
checkTTSandShowConfig();
|
||||
return true;
|
||||
});
|
||||
findPreference("import").setOnPreferenceClickListener(preference -> {
|
||||
showImportDialog();
|
||||
return true;
|
||||
});
|
||||
findPreference("export").setOnPreferenceClickListener(preference -> {
|
||||
showExportDialog();
|
||||
return true;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private boolean showExportDialog(){
|
||||
private VoiceAnnouncements voiceAnnouncements;
|
||||
|
||||
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)
|
||||
.setTitle(R.string.exportData)
|
||||
.setMessage(R.string.exportDataSummary)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.backup, (dialog, which) -> {
|
||||
exportBackup();
|
||||
}).create().show();
|
||||
return true;
|
||||
.setPositiveButton(R.string.backup, (dialog, which) -> exportBackup()).create().show();
|
||||
}
|
||||
|
||||
private void exportBackup(){
|
||||
@ -182,15 +126,18 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
new Thread(() -> {
|
||||
try{
|
||||
String file= getFilesDir().getAbsolutePath() + "/shared/backup.ftb";
|
||||
new File(file).getParentFile().mkdirs();
|
||||
File parent = new File(file).getParentFile();
|
||||
if (!parent.exists() && !parent.mkdirs()) {
|
||||
throw new IOException("Cannot write");
|
||||
}
|
||||
Uri uri= FileProvider.getUriForFile(getBaseContext(), "de.tadris.fitness.fileprovider", new File(file));
|
||||
|
||||
Exporter.exportData(getBaseContext(), new File(file),
|
||||
(progress, action) -> mHandler.post(() -> dialogController.setProgress(progress, action)));
|
||||
BackupController backupController= new BackupController(getBaseContext(), new File(file), (progress, action) -> mHandler.post(() -> dialogController.setProgress(progress, action)));
|
||||
backupController.exportData();
|
||||
|
||||
mHandler.post(() -> {
|
||||
dialogController.cancel();
|
||||
shareFile(uri);
|
||||
FileUtils.saveOrShareFile(this, uri, "ftb");
|
||||
});
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
@ -202,28 +149,25 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
}).start();
|
||||
}
|
||||
|
||||
private boolean showImportDialog(){
|
||||
private void showImportDialog() {
|
||||
if(!hasPermission()){
|
||||
requestPermissions();
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.importBackup)
|
||||
.setMessage(R.string.importBackupMessage)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.restore, (dialog, which) -> {
|
||||
importBackup();
|
||||
}).create().show();
|
||||
return true;
|
||||
.setPositiveButton(R.string.restore, (dialog, which) -> importBackup()).create().show();
|
||||
}
|
||||
|
||||
void requestPermissions(){
|
||||
private void requestPermissions() {
|
||||
if (!hasPermission()) {
|
||||
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 10);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasPermission(){
|
||||
private boolean hasPermission() {
|
||||
return ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
@ -239,12 +183,10 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
switch (requestCode) {
|
||||
case FILE_SELECT_CODE:
|
||||
if (resultCode == RESULT_OK){
|
||||
importBackup(data.getData());
|
||||
}
|
||||
break;
|
||||
if (requestCode == FILE_SELECT_CODE) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
importBackup(data.getData());
|
||||
}
|
||||
}
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
@ -254,10 +196,10 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
dialogController.show();
|
||||
new Thread(() -> {
|
||||
try{
|
||||
Exporter.importData(getBaseContext(), uri,
|
||||
RestoreController restoreController= new RestoreController(getBaseContext(), uri,
|
||||
(progress, action) -> mHandler.post(() -> dialogController.setProgress(progress, action)));
|
||||
restoreController.restoreData();
|
||||
|
||||
// DO on backup finished
|
||||
mHandler.post(dialogController::cancel);
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
@ -269,7 +211,7 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
}).start();
|
||||
}
|
||||
|
||||
private boolean showWeightPicker() {
|
||||
private void showWeightPicker() {
|
||||
UnitUtils.setUnit(this); // Maybe the user changed unit system
|
||||
|
||||
final AlertDialog.Builder d = new AlertDialog.Builder(this);
|
||||
@ -280,7 +222,8 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
np.setMaxValue((int) UnitUtils.CHOSEN_SYSTEM.getWeightFromKilogram(150));
|
||||
np.setMinValue((int) UnitUtils.CHOSEN_SYSTEM.getWeightFromKilogram(20));
|
||||
np.setFormatter(value -> value + " " + UnitUtils.CHOSEN_SYSTEM.getWeightUnit());
|
||||
np.setValue((int)Math.round(UnitUtils.CHOSEN_SYSTEM.getWeightFromKilogram(preferences.getInt("weight", 80))));
|
||||
final String preferenceVariable = "weight";
|
||||
np.setValue((int)Math.round(UnitUtils.CHOSEN_SYSTEM.getWeightFromKilogram(preferences.getInt(preferenceVariable, 80))));
|
||||
np.setWrapSelectorWheel(false);
|
||||
|
||||
d.setView(v);
|
||||
@ -289,12 +232,10 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
d.setPositiveButton(R.string.okay, (dialog, which) -> {
|
||||
int unitValue= np.getValue();
|
||||
int kilograms= (int)Math.round(UnitUtils.CHOSEN_SYSTEM.getKilogramFromUnit(unitValue));
|
||||
preferences.edit().putInt("weight", kilograms).apply();
|
||||
preferences.edit().putInt(preferenceVariable, kilograms).apply();
|
||||
});
|
||||
|
||||
d.create().show();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -308,24 +249,4 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
}
|
||||
}
|
||||
|
||||
@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) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -20,25 +20,20 @@
|
||||
package de.tadris.fitness.activity;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Typeface;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
@ -50,6 +45,7 @@ import de.tadris.fitness.data.WorkoutSample;
|
||||
import de.tadris.fitness.osm.OAuthAuthentication;
|
||||
import de.tadris.fitness.osm.OsmTraceUploader;
|
||||
import de.tadris.fitness.util.DialogUtils;
|
||||
import de.tadris.fitness.util.FileUtils;
|
||||
import de.tadris.fitness.util.gpx.GpxExporter;
|
||||
import de.tadris.fitness.util.unit.UnitUtils;
|
||||
import de.tadris.fitness.view.ProgressDialogController;
|
||||
@ -58,6 +54,7 @@ import oauth.signpost.OAuthConsumer;
|
||||
|
||||
public class ShowWorkoutActivity extends WorkoutActivity implements DialogUtils.WorkoutDeleter {
|
||||
|
||||
TextView commentView;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@ -66,14 +63,13 @@ public class ShowWorkoutActivity extends WorkoutActivity implements DialogUtils.
|
||||
initBeforeContent();
|
||||
|
||||
setContentView(R.layout.activity_show_workout);
|
||||
root= findViewById(R.id.showWorkoutRoot);
|
||||
initRoot();
|
||||
|
||||
initAfterContent();
|
||||
|
||||
addText(getString(R.string.comment) + ": " + workout.comment).setOnClickListener(v -> {
|
||||
TextView textView= (TextView)v;
|
||||
openEditCommentDialog(textView);
|
||||
});
|
||||
commentView = addText("", true);
|
||||
commentView.setOnClickListener(v -> openEditCommentDialog());
|
||||
updateCommentText();
|
||||
|
||||
addTitle(getString(R.string.workoutTime));
|
||||
addKeyValue(getString(R.string.workoutDate), getDate());
|
||||
@ -84,110 +80,91 @@ public class ShowWorkoutActivity extends WorkoutActivity implements DialogUtils.
|
||||
|
||||
addKeyValue(getString(R.string.workoutDistance), UnitUtils.getDistance(workout.length), getString(R.string.workoutPace), UnitUtils.getPace(workout.avgPace));
|
||||
|
||||
addTitle(getString(R.string.workoutRoute));
|
||||
if (hasSamples()) {
|
||||
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));
|
||||
|
||||
addKeyValue(getString(R.string.workoutAvgSpeed), UnitUtils.getSpeed(workout.avgSpeed),
|
||||
addKeyValue(getString(R.string.workoutAvgSpeedShort), UnitUtils.getSpeed(workout.avgSpeed),
|
||||
getString(R.string.workoutTopSpeed), UnitUtils.getSpeed(workout.topSpeed));
|
||||
|
||||
addSpeedDiagram();
|
||||
if (hasSamples()) {
|
||||
|
||||
speedDiagram.setOnClickListener(v -> startDiagramActivity(ShowWorkoutMapDiagramActivity.DIAGRAM_TYPE_SPEED));
|
||||
addSpeedDiagram();
|
||||
|
||||
speedDiagram.setOnClickListener(v -> startDiagramActivity(ShowWorkoutMapDiagramActivity.DIAGRAM_TYPE_SPEED));
|
||||
}
|
||||
|
||||
addTitle(getString(R.string.workoutBurnedEnergy));
|
||||
addKeyValue(getString(R.string.workoutTotalEnergy), workout.calorie + " kcal",
|
||||
getString(R.string.workoutEnergyConsumption), UnitUtils.getRelativeEnergyConsumption((double)workout.calorie / ((double)workout.length / 1000)));
|
||||
|
||||
addTitle(getString(R.string.height));
|
||||
if (hasSamples()) {
|
||||
addTitle(getString(R.string.height));
|
||||
|
||||
addKeyValue(getString(R.string.workoutAscent), UnitUtils.getDistance((int)workout.ascent),
|
||||
getString(R.string.workoutDescent), UnitUtils.getDistance((int)workout.descent));
|
||||
addKeyValue(getString(R.string.workoutAscent), UnitUtils.getDistance((int) workout.ascent),
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void startDiagramActivity(String diagramType){
|
||||
private void startDiagramActivity(String diagramType) {
|
||||
ShowWorkoutMapDiagramActivity.DIAGRAM_TYPE= diagramType;
|
||||
startActivity(new Intent(ShowWorkoutActivity.this, ShowWorkoutMapDiagramActivity.class));
|
||||
}
|
||||
|
||||
|
||||
void openEditCommentDialog(final TextView change){
|
||||
private void openEditCommentDialog() {
|
||||
final EditText editText= new EditText(this);
|
||||
editText.setText(workout.comment);
|
||||
editText.setSingleLine(true);
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.enterComment)
|
||||
.setPositiveButton(R.string.okay, (dialog, which) -> changeComment(editText.getText().toString(), change))
|
||||
.setPositiveButton(R.string.okay, (dialog, which) -> changeComment(editText.getText().toString()))
|
||||
.setView(editText).create().show();
|
||||
}
|
||||
|
||||
void changeComment(String comment, TextView onChange){
|
||||
private void changeComment(String comment) {
|
||||
workout.comment= comment;
|
||||
Instance.getInstance(this).db.workoutDao().updateWorkout(workout);
|
||||
onChange.setText(getString(R.string.comment) + ": " + workout.comment);
|
||||
updateCommentText();
|
||||
}
|
||||
|
||||
String getDate(){
|
||||
private void updateCommentText() {
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
// Inflate the menu; this adds items to the action bar if it is present.
|
||||
@ -205,19 +182,27 @@ public class ShowWorkoutActivity extends WorkoutActivity implements DialogUtils.
|
||||
}
|
||||
|
||||
private void exportToGpx(){
|
||||
if (!hasStoragePermission()) {
|
||||
requestStoragePermissions();
|
||||
return;
|
||||
}
|
||||
ProgressDialogController dialogController= new ProgressDialogController(this, getString(R.string.exporting));
|
||||
dialogController.setIndeterminate(true);
|
||||
dialogController.show();
|
||||
new Thread(() -> {
|
||||
try{
|
||||
String file= getFilesDir().getAbsolutePath() + "/shared/workout.gpx";
|
||||
new File(file).getParentFile().mkdirs();
|
||||
File parent = new File(file).getParentFile();
|
||||
if (!parent.exists() && !parent.mkdirs()) {
|
||||
throw new IOException("Cannot write to " + file);
|
||||
}
|
||||
Uri uri= FileProvider.getUriForFile(getBaseContext(), "de.tadris.fitness.fileprovider", new File(file));
|
||||
|
||||
GpxExporter.exportWorkout(getBaseContext(), workout, new File(file));
|
||||
dialogController.cancel();
|
||||
mHandler.post(() -> shareFile(uri));
|
||||
mHandler.post(() -> FileUtils.saveOrShareFile(this, uri, "gpx"));
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
mHandler.post(() -> showErrorDialog(e, R.string.error, R.string.errorGpxExportFailed));
|
||||
}
|
||||
}).start();
|
||||
@ -245,7 +230,7 @@ public class ShowWorkoutActivity extends WorkoutActivity implements DialogUtils.
|
||||
authentication.authenticateIfNecessary();
|
||||
}
|
||||
|
||||
AlertDialog dialog= null;
|
||||
private AlertDialog dialog = null;
|
||||
private void showUploadOptions(){
|
||||
dialog= new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.actionUploadToOSM)
|
||||
@ -286,8 +271,15 @@ public class ShowWorkoutActivity extends WorkoutActivity implements DialogUtils.
|
||||
case R.id.actionUploadOSM:
|
||||
prepareUpload();
|
||||
return true;
|
||||
case R.id.actionEditComment:
|
||||
openEditCommentDialog();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
void initRoot() {
|
||||
root = findViewById(R.id.showWorkoutRoot);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -32,7 +32,7 @@ public class ShowWorkoutMapActivity extends WorkoutActivity {
|
||||
initBeforeContent();
|
||||
|
||||
setContentView(R.layout.activity_show_workout_map);
|
||||
root= findViewById(R.id.showWorkoutMapParent);
|
||||
initRoot();
|
||||
|
||||
initAfterContent();
|
||||
|
||||
@ -43,4 +43,8 @@ public class ShowWorkoutMapActivity extends WorkoutActivity {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
void initRoot() {
|
||||
root = findViewById(R.id.showWorkoutMapParent);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -36,7 +36,7 @@ public class ShowWorkoutMapDiagramActivity extends WorkoutActivity {
|
||||
initBeforeContent();
|
||||
|
||||
setContentView(R.layout.activity_show_workout_map_diagram);
|
||||
root= findViewById(R.id.showWorkoutMapParent);
|
||||
initRoot();
|
||||
|
||||
initAfterContent();
|
||||
|
||||
@ -54,4 +54,8 @@ public class ShowWorkoutMapDiagramActivity extends WorkoutActivity {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
void initRoot() {
|
||||
root = findViewById(R.id.showWorkoutMapParent);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -55,46 +55,45 @@ import de.tadris.fitness.data.WorkoutManager;
|
||||
import de.tadris.fitness.data.WorkoutSample;
|
||||
import de.tadris.fitness.map.MapManager;
|
||||
import de.tadris.fitness.map.WorkoutLayer;
|
||||
import de.tadris.fitness.map.tilesource.TileSources;
|
||||
import de.tadris.fitness.util.ThemeManager;
|
||||
import de.tadris.fitness.util.WorkoutTypeCalculator;
|
||||
import de.tadris.fitness.util.unit.UnitUtils;
|
||||
|
||||
public abstract class WorkoutActivity extends FitoTrackActivity {
|
||||
public abstract class WorkoutActivity extends InformationActivity {
|
||||
|
||||
public static Workout selectedWorkout;
|
||||
|
||||
protected List<WorkoutSample> samples;
|
||||
protected Workout workout;
|
||||
protected ViewGroup root;
|
||||
protected Resources.Theme theme;
|
||||
protected MapView map;
|
||||
protected TileDownloadLayer downloadLayer;
|
||||
protected FixedPixelCircle highlightingCircle;
|
||||
protected Handler mHandler= new Handler();
|
||||
List<WorkoutSample> samples;
|
||||
Workout workout;
|
||||
private Resources.Theme theme;
|
||||
MapView map;
|
||||
private TileDownloadLayer downloadLayer;
|
||||
private FixedPixelCircle highlightingCircle;
|
||||
final Handler mHandler = new Handler();
|
||||
|
||||
protected LineChart speedDiagram, heightDiagram;
|
||||
LineChart speedDiagram;
|
||||
LineChart heightDiagram;
|
||||
|
||||
protected void initBeforeContent(){
|
||||
void initBeforeContent() {
|
||||
workout= selectedWorkout;
|
||||
samples= Arrays.asList(Instance.getInstance(this).db.workoutDao().getAllSamplesOfWorkout(workout.id));
|
||||
setTheme(ThemeManager.getThemeByWorkout(workout));
|
||||
setTheme(Instance.getInstance(this).themes.getWorkoutTypeTheme(workout.getWorkoutType()));
|
||||
}
|
||||
|
||||
protected void initAfterContent(){
|
||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
setTitle(WorkoutTypeCalculator.getType(workout));
|
||||
void initAfterContent() {
|
||||
if (getActionBar() != null) {
|
||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
setTitle(workout.getWorkoutType().title);
|
||||
|
||||
theme= getTheme();
|
||||
}
|
||||
|
||||
void addDiagram(SampleConverter converter){
|
||||
private void addDiagram(SampleConverter converter) {
|
||||
root.addView(getDiagram(converter), new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, fullScreenItems ? ViewGroup.LayoutParams.MATCH_PARENT : getWindowManager().getDefaultDisplay().getWidth()*3/4));
|
||||
}
|
||||
|
||||
protected boolean diagramsInteractive= false;
|
||||
boolean diagramsInteractive = false;
|
||||
|
||||
LineChart getDiagram(SampleConverter converter){
|
||||
private LineChart getDiagram(SampleConverter converter) {
|
||||
LineChart chart= new LineChart(this);
|
||||
|
||||
converter.onCreate();
|
||||
@ -112,7 +111,7 @@ public abstract class WorkoutActivity extends FitoTrackActivity {
|
||||
dataSet.setValueTextColor(getThemePrimaryColor());
|
||||
dataSet.setDrawCircles(false);
|
||||
dataSet.setLineWidth(4);
|
||||
dataSet.setMode(LineDataSet.Mode.CUBIC_BEZIER);
|
||||
dataSet.setMode(LineDataSet.Mode.HORIZONTAL_BEZIER);
|
||||
|
||||
Description description= new Description();
|
||||
description.setText(converter.getDescription());
|
||||
@ -145,7 +144,7 @@ public abstract class WorkoutActivity extends FitoTrackActivity {
|
||||
return chart;
|
||||
}
|
||||
|
||||
protected void onDiagramValueSelected(LatLong latLong){
|
||||
private void onDiagramValueSelected(LatLong latLong) {
|
||||
Paint p= AndroidGraphicFactory.INSTANCE.createPaint();
|
||||
p.setColor(0xff693cff);
|
||||
highlightingCircle= new FixedPixelCircle(latLong, 20, p, null);
|
||||
@ -166,7 +165,7 @@ public abstract class WorkoutActivity extends FitoTrackActivity {
|
||||
void afterAdd(LineChart chart);
|
||||
}
|
||||
|
||||
WorkoutSample findSample(SampleConverter converter, Entry entry){
|
||||
private WorkoutSample findSample(SampleConverter converter, Entry entry) {
|
||||
for(WorkoutSample sample : samples){
|
||||
if(converter.compare(sample, entry)){
|
||||
return sample;
|
||||
@ -251,12 +250,12 @@ public abstract class WorkoutActivity extends FitoTrackActivity {
|
||||
});
|
||||
}
|
||||
|
||||
protected boolean fullScreenItems = false;
|
||||
protected LinearLayout mapRoot;
|
||||
boolean fullScreenItems = false;
|
||||
LinearLayout mapRoot;
|
||||
|
||||
void addMap(){
|
||||
map= new MapView(this);
|
||||
downloadLayer= MapManager.setupMap(map, TileSources.Purpose.DEFAULT);
|
||||
downloadLayer = MapManager.setupMap(map);
|
||||
|
||||
WorkoutLayer workoutLayer= new WorkoutLayer(samples, getThemePrimaryColor());
|
||||
map.addLayer(workoutLayer);
|
||||
@ -290,13 +289,19 @@ public abstract class WorkoutActivity extends FitoTrackActivity {
|
||||
|
||||
}
|
||||
|
||||
int getMapHeight(){
|
||||
private int getMapHeight() {
|
||||
return getWindowManager().getDefaultDisplay().getWidth()*3/4;
|
||||
}
|
||||
|
||||
protected boolean hasSamples() {
|
||||
return samples.size() > 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
map.destroyAll();
|
||||
if (map != null) {
|
||||
map.destroyAll();
|
||||
}
|
||||
AndroidGraphicFactory.clearResourceMemoryCache();
|
||||
super.onDestroy();
|
||||
}
|
||||
@ -304,12 +309,16 @@ public abstract class WorkoutActivity extends FitoTrackActivity {
|
||||
@Override
|
||||
public void onPause(){
|
||||
super.onPause();
|
||||
downloadLayer.onPause();
|
||||
if (downloadLayer != null) {
|
||||
downloadLayer.onPause();
|
||||
}
|
||||
}
|
||||
|
||||
public void onResume(){
|
||||
super.onResume();
|
||||
downloadLayer.onResume();
|
||||
if (downloadLayer != null) {
|
||||
downloadLayer.onResume();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -22,7 +22,7 @@ package de.tadris.fitness.data;
|
||||
import androidx.room.Database;
|
||||
import androidx.room.RoomDatabase;
|
||||
|
||||
@Database(version = 2, entities = {Workout.class, WorkoutSample.class})
|
||||
@Database(version = 3, entities = {Workout.class, WorkoutSample.class})
|
||||
public abstract class AppDatabase extends RoomDatabase {
|
||||
public abstract WorkoutDao workoutDao();
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -25,7 +25,7 @@ import android.preference.PreferenceManager;
|
||||
|
||||
public class UserPreferences {
|
||||
|
||||
private SharedPreferences preferences;
|
||||
private final SharedPreferences preferences;
|
||||
|
||||
public UserPreferences(Context context) {
|
||||
this.preferences= PreferenceManager.getDefaultSharedPreferences(context);
|
||||
@ -35,6 +35,14 @@ public class UserPreferences {
|
||||
return preferences.getInt("weight", 80);
|
||||
}
|
||||
|
||||
public int getSpokenUpdateTimePeriod(){
|
||||
return preferences.getInt("spokenUpdateTimePeriod", 0);
|
||||
}
|
||||
|
||||
public int getSpokenUpdateDistancePeriod(){
|
||||
return preferences.getInt("spokenUpdateDistancePeriod", 0);
|
||||
}
|
||||
|
||||
public String getMapStyle(){
|
||||
return preferences.getString("mapStyle", "osm.mapnik");
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -19,10 +19,13 @@
|
||||
|
||||
package de.tadris.fitness.data;
|
||||
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.PrimaryKey;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
@ -31,10 +34,6 @@ import java.util.Date;
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class Workout{
|
||||
|
||||
public static final String WORKOUT_TYPE_RUNNING= "running";
|
||||
public static final String WORKOUT_TYPE_HIKING= "hiking";
|
||||
public static final String WORKOUT_TYPE_CYCLING= "cycling";
|
||||
|
||||
@PrimaryKey
|
||||
public long id;
|
||||
|
||||
@ -63,12 +62,13 @@ public class Workout{
|
||||
public double topSpeed;
|
||||
|
||||
/**
|
||||
* Average pace of workout in km/min
|
||||
* Average pace of workout in min/km
|
||||
*/
|
||||
public double avgPace;
|
||||
|
||||
public String workoutType;
|
||||
|
||||
@ColumnInfo(name = "workoutType")
|
||||
@JsonProperty(value = "workoutType")
|
||||
public String workoutTypeId;
|
||||
|
||||
public float ascent;
|
||||
|
||||
@ -76,6 +76,8 @@ public class Workout{
|
||||
|
||||
public int calorie;
|
||||
|
||||
public boolean edited;
|
||||
|
||||
public String toString(){
|
||||
if(comment.length() > 2){
|
||||
return comment;
|
||||
@ -84,9 +86,20 @@ public class Workout{
|
||||
}
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public String getDateString(){
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
122
app/src/main/java/de/tadris/fitness/data/WorkoutBuilder.java
Normal file
122
app/src/main/java/de/tadris/fitness/data/WorkoutBuilder.java
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -19,118 +19,10 @@
|
||||
|
||||
package de.tadris.fitness.data;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.SensorManager;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import de.tadris.fitness.Instance;
|
||||
import de.tadris.fitness.util.CalorieCalculator;
|
||||
|
||||
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){
|
||||
for(int i= 0; i < samples.size(); i++){
|
||||
WorkoutSample sample= samples.get(i);
|
||||
|
||||
59
app/src/main/java/de/tadris/fitness/data/WorkoutType.java
Normal file
59
app/src/main/java/de/tadris/fitness/data/WorkoutType.java
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
* FitoTrack is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* FitoTrack is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.tadris.fitness.dialog;
|
||||
|
||||
import android.app.DatePickerDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.DialogFragment;
|
||||
import android.os.Bundle;
|
||||
import android.widget.DatePicker;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
public class DatePickerFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener {
|
||||
|
||||
public DatePickerCallback callback;
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
// Use the current date as the default date in the picker
|
||||
final Calendar c = Calendar.getInstance();
|
||||
c.setTimeInMillis(System.currentTimeMillis());
|
||||
int year = c.get(Calendar.YEAR);
|
||||
int month = c.get(Calendar.MONTH);
|
||||
int day = c.get(Calendar.DAY_OF_MONTH);
|
||||
|
||||
// Create a new instance of DatePickerDialog and return it
|
||||
return new DatePickerDialog(getActivity(), this, year, month, day);
|
||||
}
|
||||
|
||||
public void onDateSet(DatePicker view, int year, int month, int day) {
|
||||
callback.onDatePick(year, month, day);
|
||||
}
|
||||
|
||||
public interface DatePickerCallback {
|
||||
void onDatePick(int year, int month, int day);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
* FitoTrack is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* FitoTrack is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.tadris.fitness.dialog;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.widget.ArrayAdapter;
|
||||
|
||||
import de.tadris.fitness.R;
|
||||
import de.tadris.fitness.data.WorkoutType;
|
||||
|
||||
public class SelectWorkoutTypeDialog {
|
||||
|
||||
private Activity context;
|
||||
private WorkoutTypeSelectListener listener;
|
||||
private WorkoutType[] options;
|
||||
|
||||
public SelectWorkoutTypeDialog(Activity context, WorkoutTypeSelectListener listener) {
|
||||
this.context = context;
|
||||
this.listener = listener;
|
||||
this.options = WorkoutType.values();
|
||||
}
|
||||
|
||||
public void show() {
|
||||
AlertDialog.Builder builderSingle = new AlertDialog.Builder(context);
|
||||
|
||||
final ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(context, R.layout.select_dialog_singlechoice_material);
|
||||
for (WorkoutType type : options) {
|
||||
arrayAdapter.add(context.getString(type.title));
|
||||
}
|
||||
|
||||
builderSingle.setAdapter(arrayAdapter, (dialog, which) -> listener.onSelectWorkoutType(options[which]));
|
||||
builderSingle.show();
|
||||
}
|
||||
|
||||
public interface WorkoutTypeSelectListener {
|
||||
void onSelectWorkoutType(WorkoutType workoutType);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
* FitoTrack is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* FitoTrack is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.tadris.fitness.dialog;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.DialogFragment;
|
||||
import android.app.TimePickerDialog;
|
||||
import android.os.Bundle;
|
||||
import android.text.format.DateFormat;
|
||||
import android.widget.TimePicker;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
public class TimePickerFragment extends DialogFragment implements TimePickerDialog.OnTimeSetListener {
|
||||
|
||||
public TimePickerCallback callback;
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
// Use the current time as the default values for the picker
|
||||
final Calendar c = Calendar.getInstance();
|
||||
int hour = c.get(Calendar.HOUR_OF_DAY);
|
||||
int minute = c.get(Calendar.MINUTE);
|
||||
|
||||
// Create a new instance of TimePickerDialog and return it
|
||||
return new TimePickerDialog(getActivity(), this, hour, minute, DateFormat.is24HourFormat(getActivity()));
|
||||
}
|
||||
|
||||
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
|
||||
callback.onTimePick(hourOfDay, minute);
|
||||
}
|
||||
|
||||
public interface TimePickerCallback {
|
||||
void onTimePick(int hour, int minute);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -17,7 +17,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.tadris.fitness.util.export;
|
||||
package de.tadris.fitness.export;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
|
||||
@ -29,20 +29,18 @@ import de.tadris.fitness.data.WorkoutSample;
|
||||
|
||||
@JacksonXmlRootElement(localName = "fito-track")
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class FitoTrackDataContainer {
|
||||
class FitoTrackDataContainer {
|
||||
|
||||
int version;
|
||||
List<Workout> workouts;
|
||||
List<WorkoutSample> samples;
|
||||
FitoTrackSettings settings;
|
||||
private int version;
|
||||
private List<Workout> workouts;
|
||||
private List<WorkoutSample> samples;
|
||||
|
||||
public FitoTrackDataContainer(){}
|
||||
|
||||
public FitoTrackDataContainer(int version, List<Workout> workouts, List<WorkoutSample> samples, FitoTrackSettings settings) {
|
||||
public FitoTrackDataContainer(int version, List<Workout> workouts, List<WorkoutSample> samples) {
|
||||
this.version = version;
|
||||
this.workouts = workouts;
|
||||
this.samples = samples;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
@ -69,11 +67,5 @@ public class FitoTrackDataContainer {
|
||||
this.samples = samples;
|
||||
}
|
||||
|
||||
public FitoTrackSettings getSettings() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
public void setSettings(FitoTrackSettings settings) {
|
||||
this.settings = settings;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -19,7 +19,6 @@
|
||||
|
||||
package de.tadris.fitness.map;
|
||||
|
||||
import org.mapsforge.core.model.LatLong;
|
||||
import org.mapsforge.map.android.graphics.AndroidGraphicFactory;
|
||||
import org.mapsforge.map.android.util.AndroidUtil;
|
||||
import org.mapsforge.map.android.view.MapView;
|
||||
@ -31,18 +30,19 @@ import de.tadris.fitness.map.tilesource.FitoTrackTileSource;
|
||||
import de.tadris.fitness.map.tilesource.HumanitarianTileSource;
|
||||
import de.tadris.fitness.map.tilesource.MapnikTileSource;
|
||||
import de.tadris.fitness.map.tilesource.ThunderforestTileSource;
|
||||
import de.tadris.fitness.map.tilesource.TileSources;
|
||||
|
||||
public class MapManager {
|
||||
|
||||
public static TileDownloadLayer setupMap(MapView mapView, TileSources.Purpose purpose){
|
||||
public static TileDownloadLayer setupMap(MapView mapView) {
|
||||
FitoTrackTileSource tileSource;
|
||||
|
||||
String chosenTileLayer= Instance.getInstance(mapView.getContext()).userPreferences.getMapStyle();
|
||||
switch (chosenTileLayer){
|
||||
case "osm.humanitarian": tileSource= HumanitarianTileSource.INSTANCE; break;
|
||||
case "thunderforest.outdoors": tileSource= ThunderforestTileSource.OUTDOORS; break;
|
||||
case "thunderforest.cycle": tileSource= ThunderforestTileSource.CYLE_MAP; break;
|
||||
case "thunderforest.cycle":
|
||||
tileSource = ThunderforestTileSource.CYCLE_MAP;
|
||||
break;
|
||||
default: tileSource= MapnikTileSource.INSTANCE; break; // Inclusive "osm.mapnik"
|
||||
}
|
||||
tileSource.setUserAgent("mapsforge-android");
|
||||
@ -62,7 +62,6 @@ public class MapManager {
|
||||
mapView.getLayerManager().redrawLayers();
|
||||
|
||||
mapView.setZoomLevel((byte) 18);
|
||||
mapView.setCenter(new LatLong(52, 13));
|
||||
|
||||
return downloadLayer;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -30,7 +30,7 @@ import de.tadris.fitness.data.WorkoutSample;
|
||||
|
||||
public class WorkoutLayer extends Polyline {
|
||||
|
||||
public static Paint getDEFAULT_PAINT_STROKE(int color){
|
||||
private static Paint getDEFAULT_PAINT_STROKE(int color) {
|
||||
Paint paint= AndroidGraphicFactory.INSTANCE.createPaint();
|
||||
paint.setStyle(Style.STROKE);
|
||||
paint.setColor(color);
|
||||
@ -38,13 +38,13 @@ public class WorkoutLayer extends Polyline {
|
||||
return paint;
|
||||
}
|
||||
|
||||
List<WorkoutSample> samples;
|
||||
private final List<WorkoutSample> samples;
|
||||
|
||||
public WorkoutLayer(List<WorkoutSample> samples, int color) {
|
||||
this(getDEFAULT_PAINT_STROKE(color), samples);
|
||||
}
|
||||
|
||||
public WorkoutLayer(Paint paintStroke, List<WorkoutSample> samples) {
|
||||
private WorkoutLayer(Paint paintStroke, List<WorkoutSample> samples) {
|
||||
super(paintStroke, AndroidGraphicFactory.INSTANCE);
|
||||
this.samples = samples;
|
||||
init();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -23,7 +23,7 @@ import org.mapsforge.map.layer.download.tilesource.AbstractTileSource;
|
||||
|
||||
public abstract class FitoTrackTileSource extends AbstractTileSource {
|
||||
|
||||
public FitoTrackTileSource(String[] hostNames, int port) {
|
||||
FitoTrackTileSource(String[] hostNames, int port) {
|
||||
super(hostNames, port);
|
||||
defaultTimeToLive = 8279000;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -26,7 +26,7 @@ import java.net.URL;
|
||||
|
||||
public class HumanitarianTileSource extends FitoTrackTileSource {
|
||||
|
||||
public static HumanitarianTileSource INSTANCE= new HumanitarianTileSource(new String[]{"tile-a.openstreetmap.fr", "tile-b.openstreetmap.fr", "tile-c.openstreetmap.fr"}, 443);
|
||||
public static final 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 String PROTOCOL = "https";
|
||||
@ -34,7 +34,7 @@ public class HumanitarianTileSource extends FitoTrackTileSource {
|
||||
private static final int ZOOM_LEVEL_MIN = 0;
|
||||
private static final String NAME = "Humanitarian";
|
||||
|
||||
public HumanitarianTileSource(String[] hostNames, int port) {
|
||||
private HumanitarianTileSource(String[] hostNames, int port) {
|
||||
super(hostNames, port);
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -30,11 +30,11 @@ public class MapnikTileSource extends FitoTrackTileSource {
|
||||
"a.tile.openstreetmap.org", "b.tile.openstreetmap.org", "c.tile.openstreetmap.org"}, 443);
|
||||
private static final int PARALLEL_REQUESTS_LIMIT = 8;
|
||||
private static final String PROTOCOL = "https";
|
||||
private static final int ZOOM_LEVEL_MAX = 18;
|
||||
private static final int ZOOM_LEVEL_MAX = 19;
|
||||
private static final int ZOOM_LEVEL_MIN = 0;
|
||||
private static final String NAME = "OSM Mapnik";
|
||||
|
||||
public MapnikTileSource(String[] hostNames, int port) {
|
||||
private MapnikTileSource(String[] hostNames, int port) {
|
||||
super(hostNames, port);
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -26,19 +26,19 @@ import java.net.URL;
|
||||
|
||||
public class ThunderforestTileSource extends FitoTrackTileSource{
|
||||
|
||||
public static final String API_KEY= "87b07337e42c405db6d8d39b1c0c179e";
|
||||
private static final String API_KEY = "87b07337e42c405db6d8d39b1c0c179e";
|
||||
|
||||
public static final ThunderforestTileSource OUTDOORS = new ThunderforestTileSource("outdoors", "Outdoor");
|
||||
public static final ThunderforestTileSource CYLE_MAP = new ThunderforestTileSource("cycle", "Cycle Map");
|
||||
public static final ThunderforestTileSource CYCLE_MAP = new ThunderforestTileSource("cycle", "Cycle Map");
|
||||
private static final int PARALLEL_REQUESTS_LIMIT = 8;
|
||||
private static final String PROTOCOL = "https";
|
||||
private static final int ZOOM_LEVEL_MAX = 22;
|
||||
private static final int ZOOM_LEVEL_MAX = 19;
|
||||
private static final int ZOOM_LEVEL_MIN = 0;
|
||||
|
||||
private String mapName;
|
||||
private String name;
|
||||
private final String mapName;
|
||||
private final String name;
|
||||
|
||||
public ThunderforestTileSource(String mapName, String name) {
|
||||
private ThunderforestTileSource(String mapName, String name) {
|
||||
super(new String[]{"tile.thunderforest.com"}, 443);
|
||||
this.mapName = mapName;
|
||||
this.name = name;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -22,12 +22,12 @@ package de.tadris.fitness.osm;
|
||||
import de.tadris.fitness.data.WorkoutSample;
|
||||
import de.westnordost.osmapi.map.data.LatLon;
|
||||
|
||||
public class GpsTraceLatLong implements LatLon {
|
||||
class GpsTraceLatLong implements LatLon {
|
||||
|
||||
private final double latitude;
|
||||
private final double longitude;
|
||||
|
||||
public GpsTraceLatLong(double latitude, double longitude) {
|
||||
private GpsTraceLatLong(double latitude, double longitude) {
|
||||
this.latitude = latitude;
|
||||
this.longitude = longitude;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -39,14 +39,14 @@ import oauth.signpost.exception.OAuthException;
|
||||
|
||||
public class OAuthAuthentication {
|
||||
|
||||
private OAuthConsumer oAuthConsumer= OAuthUrlProvider.getDefaultConsumer();
|
||||
private OAuthProvider oAuthProvider= OAuthUrlProvider.getDefaultProvider();
|
||||
private final OAuthConsumer oAuthConsumer = OAuthUrlProvider.getDefaultConsumer();
|
||||
private final OAuthProvider oAuthProvider = OAuthUrlProvider.getDefaultProvider();
|
||||
|
||||
private Handler handler;
|
||||
private Activity activity;
|
||||
private ProgressDialogController dialogController;
|
||||
private SharedPreferences preferences;
|
||||
private OAuthAuthenticationListener listener;
|
||||
private final Handler handler;
|
||||
private final Activity activity;
|
||||
private final ProgressDialogController dialogController;
|
||||
private final SharedPreferences preferences;
|
||||
private final OAuthAuthenticationListener listener;
|
||||
|
||||
public OAuthAuthentication(Handler handler, Activity activity, OAuthAuthenticationListener listener) {
|
||||
this.handler = handler;
|
||||
@ -118,12 +118,12 @@ public class OAuthAuthentication {
|
||||
.putBoolean("authenticated", true).apply();
|
||||
}
|
||||
|
||||
public void clearAccessToken(){
|
||||
void clearAccessToken() {
|
||||
preferences.edit().putBoolean("authenticated", false).apply();
|
||||
}
|
||||
|
||||
private void retrieveAccessToken(String verificationCode){
|
||||
handler.post(() -> dialogController.show());
|
||||
handler.post(dialogController::show);
|
||||
try{
|
||||
oAuthProvider.retrieveAccessToken(oAuthConsumer, verificationCode);
|
||||
handler.post(() -> {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -24,7 +24,7 @@ import oauth.signpost.OAuthProvider;
|
||||
import oauth.signpost.basic.DefaultOAuthConsumer;
|
||||
import oauth.signpost.basic.DefaultOAuthProvider;
|
||||
|
||||
public class OAuthUrlProvider {
|
||||
class OAuthUrlProvider {
|
||||
|
||||
static private final String CONSUMER_KEY= "jFL9grFmAo5ZS720YDDRXdSOb7F0IZQf9lnY1PHq";
|
||||
static private final String CONSUMER_SECRET= "oH969vYW60fZLco6E09UQl3uFXqjl4siQbOL0q9q";
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -24,6 +24,8 @@ import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
@ -34,6 +36,7 @@ import de.tadris.fitness.data.Workout;
|
||||
import de.tadris.fitness.data.WorkoutSample;
|
||||
import de.tadris.fitness.view.ProgressDialogController;
|
||||
import de.westnordost.osmapi.OsmConnection;
|
||||
import de.westnordost.osmapi.common.errors.OsmAuthorizationException;
|
||||
import de.westnordost.osmapi.traces.GpsTraceDetails;
|
||||
import de.westnordost.osmapi.traces.GpsTracesDao;
|
||||
import de.westnordost.osmapi.traces.GpsTrackpoint;
|
||||
@ -43,15 +46,15 @@ public class OsmTraceUploader {
|
||||
|
||||
private static final int CUT_DISTANCE= 300;
|
||||
|
||||
private Activity activity;
|
||||
private Handler handler;
|
||||
private Workout workout;
|
||||
private List<WorkoutSample> samples;
|
||||
private GpsTraceDetails.Visibility visibility;
|
||||
private OAuthConsumer consumer;
|
||||
private boolean cut;
|
||||
private ProgressDialogController dialogController;
|
||||
private String description;
|
||||
private final Activity activity;
|
||||
private final Handler handler;
|
||||
private final Workout workout;
|
||||
private final List<WorkoutSample> samples;
|
||||
private final GpsTraceDetails.Visibility visibility;
|
||||
private final OAuthConsumer consumer;
|
||||
private final boolean cut;
|
||||
private final ProgressDialogController dialogController;
|
||||
private final 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;
|
||||
@ -85,12 +88,19 @@ public class OsmTraceUploader {
|
||||
|
||||
public void upload(){
|
||||
new Thread(() -> {
|
||||
try{
|
||||
try {
|
||||
executeTask();
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
handler.post(() -> {
|
||||
Toast.makeText(activity, R.string.uploadFailed, Toast.LENGTH_LONG).show();
|
||||
@StringRes int textRes = R.string.uploadFailed;
|
||||
|
||||
if (e instanceof OsmAuthorizationException) {
|
||||
textRes = R.string.uploadFailedOsmNotAuthorized;
|
||||
new OAuthAuthentication(handler, activity, null).clearAccessToken();
|
||||
}
|
||||
|
||||
Toast.makeText(activity, textRes, Toast.LENGTH_LONG).show();
|
||||
dialogController.cancel();
|
||||
});
|
||||
}
|
||||
@ -98,7 +108,7 @@ public class OsmTraceUploader {
|
||||
}
|
||||
|
||||
private void executeTask(){
|
||||
handler.post(() -> dialogController.show());
|
||||
handler.post(dialogController::show);
|
||||
setProgress(0);
|
||||
if(cut){ cut(); }
|
||||
setProgress(20);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -51,9 +51,9 @@ public class LocationListener extends Service {
|
||||
private static final int LOCATION_INTERVAL = 1000;
|
||||
|
||||
private class LocationChangedListener implements android.location.LocationListener {
|
||||
Location mLastLocation;
|
||||
final Location mLastLocation;
|
||||
|
||||
public LocationChangedListener(String provider) {
|
||||
LocationChangedListener(String provider) {
|
||||
Log.i(TAG, "LocationListener " + provider);
|
||||
mLastLocation = new Location(provider);
|
||||
}
|
||||
@ -83,7 +83,7 @@ public class LocationListener extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
LocationChangedListener gpsListener= new LocationChangedListener(LocationManager.GPS_PROVIDER);
|
||||
private final LocationChangedListener gpsListener = new LocationChangedListener(LocationManager.GPS_PROVIDER);
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent arg0) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -31,32 +31,21 @@ import java.util.List;
|
||||
|
||||
import de.tadris.fitness.Instance;
|
||||
import de.tadris.fitness.data.Workout;
|
||||
import de.tadris.fitness.data.WorkoutManager;
|
||||
import de.tadris.fitness.data.WorkoutSample;
|
||||
import de.tadris.fitness.data.WorkoutType;
|
||||
import de.tadris.fitness.util.CalorieCalculator;
|
||||
|
||||
public class WorkoutRecorder implements LocationListener.LocationChangeListener {
|
||||
|
||||
private static int getMinDistance(String workoutType){
|
||||
switch (workoutType){
|
||||
case Workout.WORKOUT_TYPE_HIKING:
|
||||
case Workout.WORKOUT_TYPE_RUNNING:
|
||||
return 8;
|
||||
case Workout.WORKOUT_TYPE_CYCLING:
|
||||
return 15;
|
||||
default: return 10;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int PAUSE_TIME= 10000;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
private static final int AUTO_STOP_TIMEOUT= 1000*60*60*20; // 20 minutes
|
||||
|
||||
private Context context;
|
||||
private Workout workout;
|
||||
private final Context context;
|
||||
private final Workout workout;
|
||||
private RecordingState state;
|
||||
private final List<WorkoutSample> samples= new ArrayList<>();
|
||||
private long time= 0;
|
||||
@ -65,25 +54,26 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
|
||||
private long lastPause= 0;
|
||||
private long lastSampleTime= 0;
|
||||
private double distance= 0;
|
||||
private boolean hasBegan = false;
|
||||
private boolean hasBegun = false;
|
||||
|
||||
private static final double SIGNAL_BAD_THRESHOLD= 20; // In meters
|
||||
private static final int SIGNAL_LOST_THRESHOLD= 10000; // In milliseconds
|
||||
private Location lastFix= null;
|
||||
private WorkoutRecorderListener workoutRecorderListener;
|
||||
private final WorkoutRecorderListener workoutRecorderListener;
|
||||
private GpsState gpsState= GpsState.SIGNAL_LOST;
|
||||
|
||||
public WorkoutRecorder(Context context, String workoutType, WorkoutRecorderListener workoutRecorderListener) {
|
||||
public WorkoutRecorder(Context context, WorkoutType workoutType, WorkoutRecorderListener workoutRecorderListener) {
|
||||
this.context= context;
|
||||
this.state= RecordingState.IDLE;
|
||||
this.workoutRecorderListener = workoutRecorderListener;
|
||||
|
||||
this.workout= new Workout();
|
||||
workout.edited = false;
|
||||
|
||||
// Default values
|
||||
this.workout.comment= "";
|
||||
|
||||
this.workout.workoutType= workoutType;
|
||||
this.workout.setWorkoutType(workoutType);
|
||||
}
|
||||
|
||||
public void start(){
|
||||
@ -120,7 +110,7 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
|
||||
workoutRecorderListener.onAutoStop();
|
||||
}
|
||||
}else if(timeDiff > PAUSE_TIME){
|
||||
if(state == RecordingState.RUNNING){
|
||||
if (state == RecordingState.RUNNING && gpsState != GpsState.SIGNAL_LOST) {
|
||||
pause();
|
||||
}
|
||||
}else{
|
||||
@ -135,7 +125,7 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}).start();
|
||||
}, "WorkoutWatchdog").start();
|
||||
}
|
||||
|
||||
private void checkSignalState(){
|
||||
@ -165,7 +155,7 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
|
||||
}
|
||||
}
|
||||
|
||||
public void pause(){
|
||||
private void pause() {
|
||||
if(state == RecordingState.RUNNING){
|
||||
Log.i("Recorder", "Pause");
|
||||
state= RecordingState.PAUSED;
|
||||
@ -193,7 +183,7 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
|
||||
}
|
||||
Log.i("Recorder", "Save");
|
||||
synchronized (samples){
|
||||
WorkoutManager.insertWorkout(context, workout, samples);
|
||||
new WorkoutSaver(context, workout, samples).saveWorkout();
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,53 +199,58 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
|
||||
if(isActive()){
|
||||
double distance= 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){
|
||||
WorkoutSample lastSample= samples.get(samples.size() - 1);
|
||||
distance= LocationListener.locationToLatLong(location).sphericalDistance(new LatLong(lastSample.lat, lastSample.lon));
|
||||
long timediff= lastSample.absoluteTime - location.getTime();
|
||||
if(distance < getMinDistance(workout.workoutType) && timediff < 500){
|
||||
if (distance < workout.getWorkoutType().minDistance && timediff < 500) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
lastSampleTime= System.currentTimeMillis();
|
||||
if(state == RecordingState.RUNNING && location.getTime() > workout.start){
|
||||
if(samples.size() == 2 && !hasBegan){
|
||||
lastResume= System.currentTimeMillis();
|
||||
workout.start= System.currentTimeMillis();
|
||||
lastPause= 0;
|
||||
time= 0;
|
||||
pauseTime= 0;
|
||||
this.distance= 0;
|
||||
samples.clear();
|
||||
|
||||
hasBegan = true; // Do not clear a second time
|
||||
if(samples.size() == 2 && !hasBegun){
|
||||
initialClearValues();
|
||||
hasBegun = true; // Do not clear a second time
|
||||
}
|
||||
this.distance+= distance;
|
||||
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);
|
||||
}
|
||||
|
||||
addToSamples(location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distance in meters
|
||||
*/
|
||||
public int getDistance(){
|
||||
private void addToSamples(Location 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 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;
|
||||
}
|
||||
|
||||
@ -312,7 +307,7 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener
|
||||
SIGNAL_OKAY(Color.GREEN),
|
||||
SIGNAL_BAD(Color.YELLOW);
|
||||
|
||||
public int color;
|
||||
public final int color;
|
||||
|
||||
GpsState(int color) {
|
||||
this.color = color;
|
||||
|
||||
209
app/src/main/java/de/tadris/fitness/recording/WorkoutSaver.java
Normal file
209
app/src/main/java/de/tadris/fitness/recording/WorkoutSaver.java
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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]));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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);
|
||||
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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 + ".";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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 + ".";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -17,25 +17,39 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.tadris.fitness.util;
|
||||
package de.tadris.fitness.recording.announcement;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import de.tadris.fitness.R;
|
||||
import de.tadris.fitness.data.Workout;
|
||||
import de.tadris.fitness.recording.WorkoutRecorder;
|
||||
|
||||
public class ThemeManager {
|
||||
public class AnnouncementGPSStatus extends Announcement {
|
||||
|
||||
public static int getThemeByWorkoutType(String type){
|
||||
switch (type){
|
||||
case Workout.WORKOUT_TYPE_RUNNING: return R.style.Running;
|
||||
case Workout.WORKOUT_TYPE_CYCLING: return R.style.Bicycling;
|
||||
case Workout.WORKOUT_TYPE_HIKING: return R.style.Hiking;
|
||||
default: return R.style.AppTheme;
|
||||
}
|
||||
public AnnouncementGPSStatus(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public static int getThemeByWorkout(Workout workout){
|
||||
return getThemeByWorkoutType(workout.workoutType);
|
||||
@Override
|
||||
public String getId() {
|
||||
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,47 @@
|
||||
/*
|
||||
* 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,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -17,17 +17,26 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.tadris.fitness.map.tilesource;
|
||||
package de.tadris.fitness.recording.announcement;
|
||||
|
||||
import android.content.Context;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
public class TileSources {
|
||||
public enum AnnouncementMode {
|
||||
|
||||
public static FitoTrackTileSource[] tileSources= new FitoTrackTileSource[]{
|
||||
MapnikTileSource.INSTANCE, HumanitarianTileSource.INSTANCE, ThunderforestTileSource.OUTDOORS, ThunderforestTileSource.CYLE_MAP
|
||||
};
|
||||
ALWAYS,
|
||||
HEADPHONES;
|
||||
|
||||
public enum Purpose{
|
||||
DEFAULT, OUTDOOR, CYCLING
|
||||
static AnnouncementMode getCurrentMode(Context context) {
|
||||
String mode = PreferenceManager.getDefaultSharedPreferences(context).getString("announcementMode", "headphones");
|
||||
assert mode != null;
|
||||
switch (mode) {
|
||||
case "always":
|
||||
return ALWAYS;
|
||||
default:
|
||||
case "headphones":
|
||||
return HEADPHONES;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -20,12 +20,13 @@
|
||||
package de.tadris.fitness.util;
|
||||
|
||||
import de.tadris.fitness.data.Workout;
|
||||
import de.tadris.fitness.data.WorkoutType;
|
||||
|
||||
public class CalorieCalculator {
|
||||
|
||||
/**
|
||||
*
|
||||
* workoutType, duration and avgSpeed of workout have to be set
|
||||
* workoutType, duration, ascent and avgSpeed of workout have to be set
|
||||
*
|
||||
* @param workout the workout
|
||||
* @param weight the weight of the person in kilogram
|
||||
@ -43,21 +44,18 @@ public class CalorieCalculator {
|
||||
* workoutType and avgSpeed of workout have to be set
|
||||
*
|
||||
* Calculation currently ignores height.
|
||||
*
|
||||
* @param workout
|
||||
* @return MET
|
||||
*/
|
||||
public static double getMET(Workout workout){
|
||||
private static double getMET(Workout workout) {
|
||||
double speedInKmh= workout.avgSpeed * 3.6;
|
||||
if(workout.workoutType.equals(Workout.WORKOUT_TYPE_RUNNING) || workout.workoutType.equals(Workout.WORKOUT_TYPE_HIKING)){
|
||||
// This is a linear graph based on the website linked above
|
||||
WorkoutType type = workout.getWorkoutType();
|
||||
if (type == WorkoutType.RUNNING || type == WorkoutType.HIKING) {
|
||||
return Math.max(1.5, speedInKmh*1.117 - 2.1906);
|
||||
}
|
||||
if(workout.workoutType.equals(Workout.WORKOUT_TYPE_CYCLING)){
|
||||
// This is a linear graph based on the website linked above
|
||||
if (type == WorkoutType.CYCLING) {
|
||||
return Math.max(3, (speedInKmh-10) / 1.5);
|
||||
}
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
78
app/src/main/java/de/tadris/fitness/util/FileUtils.java
Normal file
78
app/src/main/java/de/tadris/fitness/util/FileUtils.java
Normal file
@ -0,0 +1,78 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -28,7 +28,7 @@ import de.tadris.fitness.R;
|
||||
|
||||
public class NotificationHelper {
|
||||
|
||||
public static String CHANNEL_WORKOUT= "workout";
|
||||
public static final String CHANNEL_WORKOUT = "workout";
|
||||
|
||||
public static void createChannels(Context context){
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
|
||||
@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
* FitoTrack is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* FitoTrack is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.tadris.fitness.util;
|
||||
|
||||
import de.tadris.fitness.R;
|
||||
import de.tadris.fitness.data.Workout;
|
||||
|
||||
public class WorkoutTypeCalculator {
|
||||
|
||||
public static int getType(Workout workout){
|
||||
if(workout.workoutType.equals(Workout.WORKOUT_TYPE_RUNNING)){
|
||||
if(workout.avgSpeed < 1.9){
|
||||
return R.string.workoutTypeWalking;
|
||||
}else if(workout.avgSpeed < 2.7){
|
||||
return R.string.workoutTypeJogging;
|
||||
}else{
|
||||
return R.string.workoutTypeRunning;
|
||||
}
|
||||
}
|
||||
if(workout.workoutType.equals(Workout.WORKOUT_TYPE_CYCLING)){
|
||||
return R.string.workoutTypeCycling;
|
||||
}
|
||||
if(workout.workoutType.equals(Workout.WORKOUT_TYPE_HIKING)){
|
||||
return R.string.workoutTypeHiking;
|
||||
}
|
||||
return R.string.workoutTypeUnknown;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
* FitoTrack is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* FitoTrack is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.tadris.fitness.util.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,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
* FitoTrack is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* FitoTrack is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.tadris.fitness.util.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) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -37,7 +37,7 @@ public class Gpx {
|
||||
Metadata metadata;
|
||||
|
||||
String name;
|
||||
String desc;
|
||||
private String desc;
|
||||
|
||||
@JacksonXmlElementWrapper(useWrapping = false)
|
||||
List<Track> trk;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -19,6 +19,7 @@
|
||||
|
||||
package de.tadris.fitness.util.gpx;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||
@ -40,7 +41,7 @@ public class GpxExporter {
|
||||
mapper.writeValue(file, getGpxFromWorkout(context, workout));
|
||||
}
|
||||
|
||||
public static Gpx getGpxFromWorkout(Context context, Workout workout){
|
||||
private static Gpx getGpxFromWorkout(Context context, Workout workout) {
|
||||
Gpx gpx= new Gpx();
|
||||
gpx.name= workout.toString();
|
||||
gpx.version= "1.1";
|
||||
@ -52,7 +53,7 @@ public class GpxExporter {
|
||||
return gpx;
|
||||
}
|
||||
|
||||
public static Track getTrackFromWorkout(Context context, Workout workout, int number){
|
||||
private static Track getTrackFromWorkout(Context context, Workout workout, int number) {
|
||||
WorkoutSample[] samples= Instance.getInstance(context).db.workoutDao().getAllSamplesOfWorkout(workout.id);
|
||||
Track track= new Track();
|
||||
track.number= number;
|
||||
@ -60,7 +61,7 @@ public class GpxExporter {
|
||||
track.cmt= workout.comment;
|
||||
track.desc= workout.comment;
|
||||
track.src= "FitoTrack";
|
||||
track.type= workout.workoutType;
|
||||
track.type = workout.getWorkoutType().id;
|
||||
track.trkseg= new ArrayList<>();
|
||||
|
||||
TrackSegment segment= new TrackSegment();
|
||||
@ -77,13 +78,14 @@ public class GpxExporter {
|
||||
return track;
|
||||
}
|
||||
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
private static final SimpleDateFormat formatter= new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
|
||||
|
||||
public static String getDateTime(long time){
|
||||
private static String getDateTime(long time) {
|
||||
return getDateTime(new Date(time));
|
||||
}
|
||||
|
||||
public static String getDateTime(Date date){
|
||||
private static String getDateTime(Date date) {
|
||||
return formatter.format(date);
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -21,9 +21,9 @@ package de.tadris.fitness.util.gpx;
|
||||
|
||||
public class Metadata {
|
||||
|
||||
String name;
|
||||
String desc;
|
||||
String time;
|
||||
private String name;
|
||||
private String desc;
|
||||
private String time;
|
||||
|
||||
public Metadata() {
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -21,21 +21,23 @@ package de.tadris.fitness.util.gpx;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
|
||||
public class TrackPoint {
|
||||
class TrackPoint {
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true)
|
||||
private
|
||||
double lat;
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true)
|
||||
private
|
||||
double lon;
|
||||
|
||||
double ele;
|
||||
private double ele;
|
||||
|
||||
String time;
|
||||
private String time;
|
||||
|
||||
String fix;
|
||||
private String fix;
|
||||
|
||||
TrackPointExtension extensions;
|
||||
private TrackPointExtension extensions;
|
||||
|
||||
public TrackPoint(){}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -21,7 +21,7 @@ package de.tadris.fitness.util.gpx;
|
||||
|
||||
public class TrackPointExtension {
|
||||
|
||||
double speed;
|
||||
private double speed;
|
||||
|
||||
public TrackPointExtension(){}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -23,7 +23,7 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TrackSegment {
|
||||
class TrackSegment {
|
||||
|
||||
@JacksonXmlElementWrapper(useWrapping = false)
|
||||
List<TrackPoint> trkpt;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -24,22 +24,23 @@ import android.preference.PreferenceManager;
|
||||
|
||||
public class UnitUtils {
|
||||
|
||||
public static final Unit UNITS_METRIC= new Metric();
|
||||
public static final Unit UNITS_METRIC_PHYSICAL= new MetricPhysical();
|
||||
public static final Unit UNITS_IMPERIAL_YARDS= new Imperial();
|
||||
public static final Unit UNITS_IMPERIAL_METERS= new ImperialWithMeters();
|
||||
public static final Unit[] supportedUnits= new Unit[] {
|
||||
private static final Unit UNITS_METRIC = new Metric();
|
||||
private static final Unit UNITS_METRIC_PHYSICAL = new MetricPhysical();
|
||||
private static final Unit UNITS_IMPERIAL_YARDS = new Imperial();
|
||||
private static final Unit UNITS_IMPERIAL_METERS = new ImperialWithMeters();
|
||||
private static final Unit[] supportedUnits = new Unit[]{
|
||||
UNITS_METRIC, UNITS_METRIC_PHYSICAL, UNITS_IMPERIAL_YARDS, UNITS_IMPERIAL_METERS
|
||||
};
|
||||
|
||||
public static Unit CHOSEN_SYSTEM= UNITS_METRIC;
|
||||
|
||||
public static void setUnit(Context context){
|
||||
int id= Integer.parseInt(PreferenceManager.getDefaultSharedPreferences(context).getString("unitSystem", String.valueOf(UnitUtils.UNITS_METRIC.getId())));
|
||||
setUnit(id);
|
||||
String id = PreferenceManager.getDefaultSharedPreferences(context).getString("unitSystem", String.valueOf(UnitUtils.UNITS_METRIC.getId()));
|
||||
assert id != null;
|
||||
setUnit(Integer.parseInt(id));
|
||||
}
|
||||
|
||||
public static void setUnit(int id){
|
||||
private static void setUnit(int id) {
|
||||
CHOSEN_SYSTEM= UNITS_METRIC;
|
||||
for(Unit unit : supportedUnits){
|
||||
if(id == unit.getId()){
|
||||
@ -62,47 +63,38 @@ public class UnitUtils {
|
||||
}
|
||||
|
||||
public static String getHourMinuteSecondTime(long time){
|
||||
long totalSeks= time / 1000;
|
||||
long totalMins= totalSeks / 60;
|
||||
long totalSecs = time / 1000;
|
||||
long totalMins = totalSecs / 60;
|
||||
long hours= totalMins / 60;
|
||||
long mins= totalMins % 60;
|
||||
long seks= totalSeks % 60;
|
||||
long secs = totalSecs % 60;
|
||||
String minStr= (mins < 10 ? "0" : "") + mins;
|
||||
String sekStr= (seks < 10 ? "0" : "") + seks;
|
||||
String sekStr = (secs < 10 ? "0" : "") + secs;
|
||||
return hours + ":" + minStr + ":" + sekStr;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param pace Pace in min/km
|
||||
* @return Pace
|
||||
*/
|
||||
public static String getPace(double pace){
|
||||
public static String getPace(double metricPace) {
|
||||
double one= CHOSEN_SYSTEM.getDistanceFromKilometers(1);
|
||||
return round(pace / one, 1) + " min/" + CHOSEN_SYSTEM.getLongDistanceUnit();
|
||||
double secondsTotal = 60 * metricPace / one;
|
||||
int minutes = (int) secondsTotal / 60;
|
||||
int seconds = (int) secondsTotal % 60;
|
||||
return minutes + ":" + (seconds < 10 ? "0" : "") + seconds + " min/" + CHOSEN_SYSTEM.getLongDistanceUnit();
|
||||
}
|
||||
|
||||
/**
|
||||
*CHOSEN_SYSTEM.getLongDistanceUnit()
|
||||
* @param consumption consumption in kcal/km
|
||||
* @return
|
||||
*/
|
||||
public static String getRelativeEnergyConsumption(double consumption){
|
||||
double one= CHOSEN_SYSTEM.getDistanceFromKilometers(1);
|
||||
return round(consumption / one, 2) + " kcal/" + 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();
|
||||
public static String getDistance(int distanceInMeters) {
|
||||
if (distanceInMeters >= 1000) {
|
||||
return round(CHOSEN_SYSTEM.getDistanceFromKilometers((double) distanceInMeters / 1000d), 1) + " " + CHOSEN_SYSTEM.getLongDistanceUnit();
|
||||
}else{
|
||||
return (int)units + " " + CHOSEN_SYSTEM.getShortDistanceUnit();
|
||||
return (int) CHOSEN_SYSTEM.getDistanceFromMeters(distanceInMeters) + " " + CHOSEN_SYSTEM.getShortDistanceUnit();
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,7 +107,7 @@ public class UnitUtils {
|
||||
return round(CHOSEN_SYSTEM.getSpeedFromMeterPerSecond(speed), 1) + " " + CHOSEN_SYSTEM.getSpeedUnit();
|
||||
}
|
||||
|
||||
public static double round(double d, int count){
|
||||
private static double round(double d, int count) {
|
||||
return (double)Math.round(d * Math.pow(10, count)) / Math.pow(10, count);
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -28,8 +28,8 @@ import de.tadris.fitness.R;
|
||||
|
||||
public class ProgressDialogController {
|
||||
|
||||
private Activity context;
|
||||
private Dialog dialog;
|
||||
private final Activity context;
|
||||
private final Dialog dialog;
|
||||
private TextView infoView;
|
||||
private ProgressBar progressBar;
|
||||
|
||||
@ -38,7 +38,7 @@ public class ProgressDialogController {
|
||||
setTitle(title);
|
||||
}
|
||||
|
||||
public ProgressDialogController(Activity context) {
|
||||
private ProgressDialogController(Activity context) {
|
||||
this.context = context;
|
||||
this.dialog= new Dialog(context);
|
||||
initDialog();
|
||||
@ -51,7 +51,7 @@ public class ProgressDialogController {
|
||||
progressBar= dialog.findViewById(R.id.dialogProgressBar);
|
||||
}
|
||||
|
||||
public void setTitle(String title){
|
||||
private void setTitle(String title) {
|
||||
dialog.setTitle(title);
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
@ -32,7 +32,6 @@ import java.util.Date;
|
||||
|
||||
import de.tadris.fitness.R;
|
||||
import de.tadris.fitness.data.Workout;
|
||||
import de.tadris.fitness.util.WorkoutTypeCalculator;
|
||||
import de.tadris.fitness.util.unit.UnitUtils;
|
||||
|
||||
public class WorkoutAdapter extends RecyclerView.Adapter<WorkoutAdapter.WorkoutViewHolder>{
|
||||
@ -40,10 +39,14 @@ public class WorkoutAdapter extends RecyclerView.Adapter<WorkoutAdapter.WorkoutV
|
||||
|
||||
public static class WorkoutViewHolder extends RecyclerView.ViewHolder{
|
||||
|
||||
View root;
|
||||
TextView lengthText, timeText, dateText, typeText, commentText;
|
||||
final View root;
|
||||
final TextView lengthText;
|
||||
final TextView timeText;
|
||||
final TextView dateText;
|
||||
final TextView typeText;
|
||||
final TextView commentText;
|
||||
|
||||
public WorkoutViewHolder(@NonNull View itemView) {
|
||||
WorkoutViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
this.root= itemView;
|
||||
lengthText= itemView.findViewById(R.id.workoutLength);
|
||||
@ -54,8 +57,8 @@ public class WorkoutAdapter extends RecyclerView.Adapter<WorkoutAdapter.WorkoutV
|
||||
}
|
||||
}
|
||||
|
||||
Workout[] workouts;
|
||||
WorkoutAdapterListener listener;
|
||||
private final Workout[] workouts;
|
||||
private final WorkoutAdapterListener listener;
|
||||
|
||||
public WorkoutAdapter(Workout[] workouts, WorkoutAdapterListener listener) {
|
||||
this.workouts = workouts;
|
||||
@ -74,7 +77,7 @@ public class WorkoutAdapter extends RecyclerView.Adapter<WorkoutAdapter.WorkoutV
|
||||
public void onBindViewHolder(WorkoutViewHolder holder, final int position) {
|
||||
Workout workout= workouts[position];
|
||||
holder.dateText.setText(SimpleDateFormat.getDateTimeInstance().format(new Date(workout.start)));
|
||||
holder.typeText.setText(WorkoutTypeCalculator.getType(workout));
|
||||
holder.typeText.setText(workout.getWorkoutType().title);
|
||||
if(workout.comment != null){
|
||||
if(workout.comment.length() > 33){
|
||||
holder.commentText.setText(workout.comment.substring(0, 30) + "...");
|
||||
@ -86,7 +89,7 @@ public class WorkoutAdapter extends RecyclerView.Adapter<WorkoutAdapter.WorkoutV
|
||||
}
|
||||
holder.lengthText.setText(UnitUtils.getDistance(workout.length));
|
||||
holder.timeText.setText(UnitUtils.getHourMinuteTime(workout.duration));
|
||||
holder.root.setOnClickListener(v -> listener.onItemClick(workout));
|
||||
holder.root.setOnClickListener(v -> listener.onItemClick(position, workout));
|
||||
holder.root.setOnLongClickListener(v -> {
|
||||
listener.onItemLongClick(position, workout);
|
||||
return true;
|
||||
@ -100,7 +103,7 @@ public class WorkoutAdapter extends RecyclerView.Adapter<WorkoutAdapter.WorkoutV
|
||||
}
|
||||
|
||||
public interface WorkoutAdapterListener{
|
||||
void onItemClick(Workout workout);
|
||||
void onItemClick(int pos, Workout workout);
|
||||
void onItemLongClick(int pos, Workout workout);
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
@ -20,5 +19,7 @@
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="300">
|
||||
<alpha android:fromAlpha="0" android:toAlpha="1"/>
|
||||
<alpha
|
||||
android:fromAlpha="0"
|
||||
android:toAlpha="1" />
|
||||
</set>
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
@ -20,10 +20,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#FFFFFF"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#FFFFFF">
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
|
||||
</vector>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
@ -20,11 +20,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:alpha="0.8"
|
||||
android:tint="#FFFFFF"
|
||||
android:alpha="0.8">
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94V1h-2v2.06C6.83,3.52 3.52,6.83 3.06,11H1v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94V23h2v-2.06c4.17,-0.46 7.48,-3.77 7.94,-7.94H23v-2h-2.06zM12,19c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
|
||||
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>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
@ -18,11 +18,11 @@
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:pathData="M12,12m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"
|
||||
android:fillColor="@android:color/white"/>
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,12m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0" />
|
||||
</vector>
|
||||
|
||||
32
app/src/main/res/layout/activity_enter_workout.xml
Normal file
32
app/src/main/res/layout/activity_enter_workout.xml
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
~ FitoTrack is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ FitoTrack is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".activity.EnterWorkoutActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/enterWorkoutRoot"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="15dp" />
|
||||
</ScrollView>
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
@ -19,10 +18,10 @@
|
||||
-->
|
||||
|
||||
<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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
tools:context=".activity.ListWorkoutsActivity">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
@ -36,12 +35,12 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|right"
|
||||
android:layout_margin="10dp"
|
||||
app:menu_animationDelayPerItem="50"
|
||||
app:menu_colorNormal="@color/colorPrimary"
|
||||
app:menu_colorPressed="@color/colorPrimaryDark"
|
||||
app:menu_animationDelayPerItem="50"
|
||||
app:menu_icon="@drawable/fab_add"
|
||||
app:menu_labels_showAnimation="@anim/fab_slide_in_from_right"
|
||||
app:menu_labels_hideAnimation="@anim/fab_slide_out_to_right">
|
||||
app:menu_labels_hideAnimation="@anim/fab_slide_out_to_right"
|
||||
app:menu_labels_showAnimation="@anim/fab_slide_in_from_right">
|
||||
|
||||
<com.github.clans.fab.FloatingActionButton
|
||||
android:id="@+id/workoutListRecordRunning"
|
||||
@ -51,7 +50,7 @@
|
||||
app:fab_colorNormal="@color/colorPrimaryRunning"
|
||||
app:fab_colorPressed="@color/colorPrimaryDarkRunning"
|
||||
app:fab_label="@string/workoutTypeRunning"
|
||||
app:fab_size="normal"/>
|
||||
app:fab_size="normal" />
|
||||
|
||||
<com.github.clans.fab.FloatingActionButton
|
||||
android:id="@+id/workoutListRecordHiking"
|
||||
@ -71,7 +70,17 @@
|
||||
app:fab_colorNormal="@color/colorPrimaryBicycling"
|
||||
app:fab_colorPressed="@color/colorPrimaryDarkBicycling"
|
||||
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>
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
@ -32,7 +31,7 @@
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/recordMapViewrRoot"
|
||||
android:id="@+id/recordMapViewerRoot"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
@ -56,6 +55,15 @@
|
||||
android:text="@string/gps"
|
||||
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>
|
||||
|
||||
@ -65,8 +73,8 @@
|
||||
android:layout_height="200dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginBottom="0dp"
|
||||
android:padding="10dp"
|
||||
android:orientation="vertical">
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/recordTime"
|
||||
@ -75,7 +83,7 @@
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="0:44:08"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@android:color/black"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="30sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
@ -117,7 +125,7 @@
|
||||
android:text="2,06 km"
|
||||
android:textAlignment="center"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@android:color/black"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
@ -137,7 +145,7 @@
|
||||
android:text="30 kcal"
|
||||
android:textAlignment="center"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@android:color/black"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
@ -156,7 +164,7 @@
|
||||
android:id="@+id/recordInfo3Title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/workoutAvgSpeed"
|
||||
android:text="@string/workoutAvgSpeedShort"
|
||||
android:textAlignment="center"
|
||||
android:textAllCaps="true"
|
||||
android:textStyle="bold" />
|
||||
@ -168,7 +176,7 @@
|
||||
android:text="7 km/h"
|
||||
android:textAlignment="center"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@android:color/black"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
@ -188,7 +196,7 @@
|
||||
android:text="30 kcal"
|
||||
android:textAlignment="center"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@android:color/black"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
@ -35,7 +34,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="20dp"></LinearLayout>
|
||||
android:padding="20dp" />
|
||||
|
||||
</ScrollView>
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
@ -25,7 +24,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".activity.ShowWorkoutMapActivity" >
|
||||
tools:context=".activity.ShowWorkoutMapActivity">
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline4"
|
||||
@ -58,6 +57,6 @@
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/guideline4"></LinearLayout>
|
||||
app:layout_constraintTop_toTopOf="@+id/guideline4" />
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
54
app/src/main/res/layout/dialog_duration_picker.xml
Normal file
54
app/src/main/res/layout/dialog_duration_picker.xml
Normal file
@ -0,0 +1,54 @@
|
||||
<?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,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
|
||||
40
app/src/main/res/layout/dialog_spoken_updates_picker.xml
Normal file
40
app/src/main/res/layout/dialog_spoken_updates_picker.xml
Normal file
@ -0,0 +1,40 @@
|
||||
<?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,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
@ -29,11 +28,12 @@
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:labelFor="@id/uploadDescription"
|
||||
android:text="@string/description" />
|
||||
|
||||
<EditText
|
||||
@ -48,7 +48,7 @@
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
@ -64,13 +64,6 @@
|
||||
android:spinnerMode="dropdown" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</TableLayout>
|
||||
|
||||
<CheckBox
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
@ -20,7 +19,7 @@
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent" >
|
||||
android:layout_height="fill_parent">
|
||||
|
||||
<NumberPicker
|
||||
android:id="@+id/weightPicker"
|
||||
|
||||
71
app/src/main/res/layout/enter_workout_line.xml
Normal file
71
app/src/main/res/layout/enter_workout_line.xml
Normal file
@ -0,0 +1,71 @@
|
||||
<?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>
|
||||
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
~ FitoTrack is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ FitoTrack is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@android:id/text1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableStart="?android:attr/listChoiceIndicatorSingle"
|
||||
android:drawablePadding="20dp"
|
||||
android:ellipsize="marquee"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="48dip"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:textAppearance="@android:style/TextAppearance.Material.Medium"
|
||||
android:textColor="?android:attr/textColorAlertDialogListItem" />
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
@ -42,7 +41,7 @@
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text=""
|
||||
android:textAllCaps="true"
|
||||
android:textColor="@color/textColorLight"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
@ -52,7 +51,7 @@
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text=""
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@color/textLighterBlack"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
@ -74,7 +73,7 @@
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text=""
|
||||
android:textAllCaps="true"
|
||||
android:textColor="@color/textColorLight"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
@ -84,7 +83,7 @@
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text=""
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@color/textLighterBlack"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
@ -21,8 +20,8 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="?android:selectableItemBackground">
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@ -37,7 +36,7 @@
|
||||
android:layout_weight="1"
|
||||
android:text="Aug 19, 2019 20:15"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="@color/textLighterBlack"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
@ -81,7 +80,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="27min"
|
||||
android:textColor="@color/textLighterBlack"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="30sp" />
|
||||
</LinearLayout>
|
||||
|
||||
@ -90,6 +89,6 @@
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@android:color/darker_gray"/>
|
||||
android:background="@android:color/darker_gray" />
|
||||
|
||||
</LinearLayout>
|
||||
27
app/src/main/res/menu/enter_workout_menu.xml
Normal file
27
app/src/main/res/menu/enter_workout_menu.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
~ FitoTrack is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ FitoTrack is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:id="@+id/actionEnterWorkoutAdd"
|
||||
android:icon="@drawable/ic_add_white"
|
||||
android:showAsAction="always"
|
||||
android:title="@string/save" />
|
||||
</menu>
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
@ -20,6 +19,9 @@
|
||||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:id="@+id/actionEditComment"
|
||||
android:title="@string/action_edit_comment" />
|
||||
<item
|
||||
android:id="@+id/actionExportGpx"
|
||||
android:title="@string/exportAsGpxFile" />
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
|
||||
65341
app/src/main/res/raw/geoids.csv
Normal file
65341
app/src/main/res/raw/geoids.csv
Normal file
File diff suppressed because it is too large
Load Diff
27
app/src/main/res/values-de/announcement_mode.xml
Normal file
27
app/src/main/res/values-de/announcement_mode.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
~ FitoTrack is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ FitoTrack is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
|
||||
<string-array name="pref_announcement_mode">
|
||||
<item>Immer</item>
|
||||
<item>Nur mit Kopfhörern</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019 Jannis Scheibe <jannis@tadris.de>
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ 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