forked from upstream-mirrors/OpenTracks
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 52226c8ef4 | |||
| c254a6ec75 | |||
| 0ecf4863a7 | |||
| 325196159d | |||
| 6adbf39e52 | |||
| db1a37f015 | |||
| 7ca9423fbf | |||
| 11c5ec43e2 |
+33
-11
@@ -59,16 +59,38 @@ We do not support type of movement (i.e., walking vs running).
|
||||
|
||||
## Tested Barometric Sensor Smartphones
|
||||
|
||||
There are smartphones that have a barometric sensor that OpenTracks uses to compute altitude gain and loss.
|
||||
OpenTracks derives the altitude gain and altitude loss using the internal barometric sensor of a device (if present).
|
||||
However, barometric sensor seem to be prone to a lot of noise.
|
||||
This may either be a malfunctioning sensor, a case that prevents the device from measuring, or something else.
|
||||
|
||||
In our tests there are smartphones whose barometric sensor works well and others don't. Here is a list of the smartphones tested:
|
||||
To check if the barometric sensor of a device is working with OpenTracks:
|
||||
1. Record a track (ca. 1min) with the device stationary.
|
||||
Expectation: 0m gain and 0m loss should.
|
||||
2. Record a track (ca. 1min) while shaking the device.
|
||||
Expectation: 0m gain and 0m loss should.
|
||||
3. Record a track (ca. 1min) while moving the device up (less than 3m difference between highest and lowest point).
|
||||
Expectation: 0m gain and 0m loss should.
|
||||
4. Record a track walking up some stairs or taking an elevator.
|
||||
Expectation: some gain and some loss is recorded that seems realistic.
|
||||
|
||||
| Brand | Model | Android Version | Tests |
|
||||
|--------|--------------------|-----------------|--------------------------------------------------------------------------------|
|
||||
| Google | Pixel | LineageOS 17.1 | Ok |
|
||||
| Google | Pixel 2 XL | LineageOS 17.1 | Ok on the bike handlebars but it fails when I carry it in a pocket or backpack |
|
||||
| Google | Pixel 3 | Android 11 | Failure |
|
||||
| Google | Pixel 3a | Android 11 | Ok |
|
||||
| Google | Pixel 3a | Lineage 18.1 | Ok |
|
||||
| Google | Pixel 4a | Android 11 | Ok |
|
||||
| Sony | Xperia XZ1 Compact | LineageOS 16.1 | Failure |
|
||||
If any of these tests do **not** show expected values for gain/loss, OpenTracks will not show useful values.
|
||||
|
||||
Please note that OpenTracks:
|
||||
* only records gain/loss if +/- 3m to the initial/previous of are measured to the previous
|
||||
* solely uses the barometric sensor for gain/loss; GPS is not used for this.
|
||||
|
||||
In our tests there are smartphones whose barometric sensor works well and others don't.
|
||||
|
||||
Here is a list of the smartphones tested:
|
||||
|
||||
| Brand | Model | Android Version | Tests |
|
||||
|---------|--------------------|-----------------|--------------------------------------------------------------------------------|
|
||||
| Google | Pixel | LineageOS 17.1 | Ok |
|
||||
| Google | Pixel 2 XL | LineageOS 17.1 | Ok on the bike handlebars but it fails when I carry it in a pocket or backpack |
|
||||
| Google | Pixel 3 | Android 11 | Failure |
|
||||
| Google | Pixel 3a | Android 11 | Ok |
|
||||
| Google | Pixel 3a | Lineage 18.1 | Ok |
|
||||
| Google | Pixel 4a | Android 11 | Ok |
|
||||
| Google | Nexus 4 | LineageOS 18.1 | Ok |
|
||||
| Samsung | S4 | Android 11 | Ok |
|
||||
| Sony | Xperia XZ1 Compact | LineageOS 16.1 | Failure |
|
||||
|
||||
+3
-3
@@ -64,8 +64,8 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId "de.dennisguse.opentracks"
|
||||
versionCode 5305
|
||||
versionName "v4.6.2"
|
||||
versionCode 5313
|
||||
versionName "v4.7.0"
|
||||
|
||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||
|
||||
@@ -128,7 +128,7 @@ dependencies {
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'androidx.preference:preference:1.2.0'
|
||||
implementation 'androidx.preference:preference:1.2.1'
|
||||
implementation 'androidx.documentfile:documentfile:1.0.1'
|
||||
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||
implementation 'com.google.android.material:material:1.9.0'
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
v4.7.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Statistics: distance, total time, and moving time are included between automatic segments (created due to distance too previous location too long)
|
||||
- Recorded tracks show sensor statistics again
|
||||
|
||||
Bugfix:
|
||||
- GPX exporter: for automatic segments their might be no location
|
||||
- CSV exporter: distance didn't use decimal grouping
|
||||
|
||||
|
||||
+13
-13
@@ -202,7 +202,7 @@ public class ExportImportTest {
|
||||
// given
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
TrackExporter trackExporter = TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context, contentProviderUtils);
|
||||
TrackExporter trackExporter = TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES.createTrackExporter(context, contentProviderUtils);
|
||||
|
||||
// when
|
||||
// 1. export
|
||||
@@ -238,24 +238,24 @@ public class ExportImportTest {
|
||||
TrackStatistics originalTrackStatistics = track.getTrackStatistics();
|
||||
|
||||
assertEquals(originalTrackStatistics.getTotalTime(), importedTrackStatistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(19), importedTrackStatistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(20), importedTrackStatistics.getTotalTime());
|
||||
|
||||
assertEquals(originalTrackStatistics.getMovingTime(), importedTrackStatistics.getMovingTime());
|
||||
assertEquals(Duration.ofSeconds(3), importedTrackStatistics.getMovingTime());
|
||||
assertEquals(Duration.ofSeconds(4), importedTrackStatistics.getMovingTime());
|
||||
|
||||
// Distance
|
||||
assertEquals(originalTrackStatistics.getTotalDistance(), importedTrackStatistics.getTotalDistance());
|
||||
assertEquals(125.16, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
||||
assertEquals(222238.70, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
||||
|
||||
// Speed
|
||||
assertEquals(originalTrackStatistics.getMaxSpeed(), importedTrackStatistics.getMaxSpeed());
|
||||
assertEquals(41.72, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(55559.67, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
|
||||
assertEquals(originalTrackStatistics.getAverageSpeed(), importedTrackStatistics.getAverageSpeed());
|
||||
assertEquals(6.58, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(11111.93, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
|
||||
assertEquals(originalTrackStatistics.getAverageMovingSpeed(), importedTrackStatistics.getAverageMovingSpeed());
|
||||
assertEquals(41.72, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(55559.67, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
|
||||
// Altitude
|
||||
assertEquals(originalTrackStatistics.getMinAltitude(), importedTrackStatistics.getMinAltitude(), 0.01);
|
||||
@@ -394,16 +394,16 @@ public class ExportImportTest {
|
||||
assertEquals(Instant.parse("2020-02-02T02:02:03Z"), importedTrackStatistics.getStartTime());
|
||||
assertEquals(Instant.parse("2020-02-02T02:03:23Z"), importedTrackStatistics.getStopTime());
|
||||
|
||||
assertEquals(Duration.ofSeconds(15), importedTrackStatistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(15), importedTrackStatistics.getMovingTime());
|
||||
assertEquals(Duration.ofSeconds(80), importedTrackStatistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(80), importedTrackStatistics.getMovingTime());
|
||||
|
||||
// Distance
|
||||
assertEquals(125.16, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
||||
assertEquals(222349.85, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
||||
|
||||
// Speed
|
||||
assertEquals(8.34, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(8.34, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(8.34, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||
assertEquals(2779.37, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(2779.37, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(2779.37, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||
|
||||
// Altitude
|
||||
assertEquals(10, importedTrackStatistics.getMinAltitude(), 0.01);
|
||||
|
||||
+2
-3
@@ -765,7 +765,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.1, 35.0, 1, 15);
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, gps2, 0, 1, 0, 0, 0f, 0f), contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
assertEquals(new TrackStatistics(startTime, gps2, 11113.275390625, 4, 3, 3704.4251302083335f, 0f, 0f).toString(), contentProviderUtils.getTrack(trackId).getTrackStatistics().toString());
|
||||
|
||||
|
||||
// when
|
||||
@@ -774,8 +774,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, stopTime, 0, 7, 0, 0, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
assertEquals(new TrackStatistics(startTime, stopTime, 11113.275390625, 10, 3, 3704.4251302083335f, 0f, 0f).toString(), contentProviderUtils.getTrack(trackId).getTrackStatistics().toString());
|
||||
|
||||
|
||||
// then
|
||||
|
||||
@@ -81,9 +81,9 @@ public class TrackStatisticsUpdaterTest {
|
||||
|
||||
// then
|
||||
TrackStatistics statistics = subject.getTrackStatistics();
|
||||
assertEquals(128.03, statistics.getTotalDistance().toM(), 0.01);
|
||||
assertEquals(Duration.ofSeconds(13), statistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(9), statistics.getMovingTime());
|
||||
assertEquals(142.26, statistics.getTotalDistance().toM(), 0.01);
|
||||
assertEquals(Duration.ofSeconds(14), statistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(10), statistics.getMovingTime());
|
||||
|
||||
assertEquals(2.5, statistics.getMinAltitude(), 0.01);
|
||||
assertEquals(32.5, statistics.getMaxAltitude(), 0.01);
|
||||
@@ -92,8 +92,8 @@ public class TrackStatisticsUpdaterTest {
|
||||
|
||||
assertEquals(14.226, statistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(14.226, statistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||
assertEquals(9.84, statistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(106.85f, statistics.getAverageHeartRate().getBPM(), 0.01);
|
||||
assertEquals(10.16, statistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(106.64f, statistics.getAverageHeartRate().getBPM(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -130,11 +130,11 @@ public class TrackPoint {
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isSegmentStart() {
|
||||
return type == Type.SEGMENT_START_AUTOMATIC || type == Type.SEGMENT_START_MANUAL;
|
||||
public boolean isSegmentManualStart() {
|
||||
return type == Type.SEGMENT_START_MANUAL;
|
||||
}
|
||||
|
||||
public boolean isSegmentEnd() {
|
||||
public boolean isSegmentManualEnd() {
|
||||
return type == Type.SEGMENT_END_MANUAL;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,8 +29,6 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.TrackRecordedActivity;
|
||||
@@ -114,10 +112,6 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
viewBinding = StatisticsRecordedBinding.inflate(inflater, container, false);
|
||||
|
||||
RecyclerView sensorsRecyclerView = viewBinding.statsSensorsRecyclerView;
|
||||
sensorsRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
|
||||
// sensorsRecyclerView.setAdapter(sensorsAdapter);
|
||||
|
||||
return viewBinding.getRoot();
|
||||
}
|
||||
|
||||
@@ -258,6 +252,28 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
if (sensorStatistics == null) {
|
||||
return;
|
||||
}
|
||||
//TODO
|
||||
|
||||
if (sensorStatistics.hasHeartRate()) {
|
||||
String maxBPM = String.valueOf(Math.round(sensorStatistics.getMaxHeartRate().getBPM()));
|
||||
String avgBPM = String.valueOf(Math.round(sensorStatistics.getAvgHeartRate().getBPM()));
|
||||
|
||||
viewBinding.statsHeartRateGroup.setVisibility(View.VISIBLE);
|
||||
viewBinding.statsMaxHeartRateValue.setText(maxBPM);
|
||||
viewBinding.statsAvgHeartRateValue.setText(avgBPM);
|
||||
}
|
||||
if (sensorStatistics.hasCadence()) {
|
||||
String maxRPM = String.valueOf(Math.round(sensorStatistics.getMaxCadence().getRPM()));
|
||||
String avgRPM = String.valueOf(Math.round(sensorStatistics.getAvgCadence().getRPM()));
|
||||
|
||||
viewBinding.statsCadenceGroup.setVisibility(View.VISIBLE);
|
||||
viewBinding.statsMaxCadenceValue.setText(maxRPM);
|
||||
viewBinding.statsAvgCadenceValue.setText(avgRPM);
|
||||
}
|
||||
if (sensorStatistics.hasPower()) {
|
||||
String W = String.valueOf(Math.round(sensorStatistics.getAvgPower().getW()));
|
||||
|
||||
viewBinding.statsPowerGroup.setVisibility(View.VISIBLE);
|
||||
viewBinding.statsPowerValue.setText(W);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,9 @@ public class CSVTrackExporter implements TrackExporter {
|
||||
SPEED_FORMAT.setMaximumFractionDigits(2);
|
||||
SPEED_FORMAT.setGroupingUsed(false);
|
||||
|
||||
DISTANCE_FORMAT.setMaximumFractionDigits(0);
|
||||
DISTANCE_FORMAT.setGroupingUsed(false);
|
||||
|
||||
HEARTRATE_FORMAT.setMaximumFractionDigits(0);
|
||||
HEARTRATE_FORMAT.setGroupingUsed(false);
|
||||
|
||||
|
||||
@@ -151,8 +151,12 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
if (wroteSegment) writeCloseSegment();
|
||||
writeOpenSegment();
|
||||
wroteSegment = true;
|
||||
trackDistance = trackDistance.plus(writeTrackPoint(track.getZoneOffset(), trackPoint, sensorPoints, trackDistance));
|
||||
sensorPoints.clear();
|
||||
if (trackPoint.hasLocation()) {
|
||||
trackDistance = trackDistance.plus(writeTrackPoint(track.getZoneOffset(), trackPoint, sensorPoints, trackDistance));
|
||||
sensorPoints.clear();
|
||||
} else {
|
||||
sensorPoints.add(trackPoint);
|
||||
}
|
||||
}
|
||||
case SENSORPOINT -> sensorPoints.add(trackPoint);
|
||||
case TRACKPOINT -> {
|
||||
|
||||
@@ -38,6 +38,7 @@ import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
*/
|
||||
//TODO Use null instead of Double.isInfinite
|
||||
//TODO Check that data ranges are valid (not less than zero etc.)
|
||||
//TODO Should be a Java record
|
||||
public class TrackStatistics {
|
||||
|
||||
// The min and max altitude (meters) seen on this track.
|
||||
|
||||
@@ -95,7 +95,7 @@ public class TrackStatisticsUpdater {
|
||||
*
|
||||
*/
|
||||
public void addTrackPoint(TrackPoint trackPoint) {
|
||||
if (trackPoint.isSegmentStart()) {
|
||||
if (trackPoint.isSegmentManualStart()) {
|
||||
reset(trackPoint);
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ public class TrackStatisticsUpdater {
|
||||
updateSpeed(trackPoint, lastTrackPoint);
|
||||
}
|
||||
|
||||
if (trackPoint.isSegmentEnd()) {
|
||||
if (trackPoint.isSegmentManualEnd()) {
|
||||
reset(trackPoint);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -397,23 +397,249 @@
|
||||
app:barrierDirection="bottom"
|
||||
app:constraint_referenced_ids="stats_altitude_gain_value,stats_altitude_loss_value" />
|
||||
|
||||
<!-- Horizontal line -->
|
||||
<!-- Heart Rate -->
|
||||
<View
|
||||
android:id="@+id/stats_end_horizontal_line"
|
||||
android:id="@+id/stats_heart_rate_horizontal_line"
|
||||
style="@style/HorizontalLine"
|
||||
android:layout_margin="8dp"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_altitude_barrier" />
|
||||
|
||||
<!-- Sensors data -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/stats_sensors_recycler_view"
|
||||
android:layout_width="0dp"
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:id="@+id/stats_heart_rate_group"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
android:visibility="gone"
|
||||
app:constraint_referenced_ids="stats_heart_rate_horizontal_line,stats_max_heart_rate_label,stats_max_heart_rate_value,stats_max_heart_rate_unit,stats_avg_heart_rate_label,stats_avg_heart_rate_value,stats_avg_heart_rate_unit" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_max_heart_rate_label"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/sensor_state_heart_rate_max"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_end_horizontal_line" />
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline2"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_heart_rate_horizontal_line" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_max_heart_rate_value"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/stats_max_heart_rate_unit"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_max_heart_rate_label"
|
||||
tools:text="100" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_max_heart_rate_unit"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryUnit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/sensor_state_heart_rate_unit"
|
||||
app:layout_constraintBottom_toBottomOf="@id/stats_max_heart_rate_value"
|
||||
app:layout_constraintStart_toEndOf="@id/stats_max_heart_rate_value"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline2" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_avg_heart_rate_label"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/sensor_state_heart_rate_avg"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_heart_rate_horizontal_line" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_avg_heart_rate_value"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@id/stats_avg_heart_rate_unit"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_avg_heart_rate_label"
|
||||
tools:text="100" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_avg_heart_rate_unit"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryUnit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/sensor_state_heart_rate_unit"
|
||||
app:layout_constraintBottom_toBottomOf="@id/stats_avg_heart_rate_value"
|
||||
app:layout_constraintStart_toEndOf="@id/stats_avg_heart_rate_value"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/stats_heart_rate_barrier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="bottom"
|
||||
app:constraint_referenced_ids="stats_max_heart_rate_value,stats_avg_heart_rate_value" />
|
||||
|
||||
<!-- Cadence -->
|
||||
<View
|
||||
android:id="@+id/stats_cadence_horizontal_line"
|
||||
style="@style/HorizontalLine"
|
||||
android:layout_margin="8dp"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_heart_rate_barrier" />
|
||||
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:id="@+id/stats_cadence_group"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:constraint_referenced_ids="stats_cadence_horizontal_line,stats_max_cadence_label,stats_max_cadence_value,stats_max_cadence_unit,stats_avg_cadence_label,stats_avg_cadence_value,stats_avg_cadence_unit" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_max_cadence_label"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/sensor_state_cadence_max"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline2"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_cadence_horizontal_line" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_max_cadence_value"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/stats_max_cadence_unit"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_max_cadence_label"
|
||||
tools:text="100" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_max_cadence_unit"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryUnit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/sensor_state_cadence_unit"
|
||||
app:layout_constraintBottom_toBottomOf="@id/stats_max_cadence_value"
|
||||
app:layout_constraintStart_toEndOf="@id/stats_max_cadence_value"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline2" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_avg_cadence_label"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/sensor_state_cadence_avg"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_cadence_horizontal_line" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_avg_cadence_value"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@id/stats_avg_cadence_unit"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_avg_cadence_label"
|
||||
tools:text="100" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_avg_cadence_unit"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryUnit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/sensor_state_cadence_unit"
|
||||
app:layout_constraintBottom_toBottomOf="@id/stats_avg_cadence_value"
|
||||
app:layout_constraintStart_toEndOf="@id/stats_avg_cadence_value"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/stats_cadence_barrier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="bottom"
|
||||
app:constraint_referenced_ids="stats_max_cadence_value,stats_avg_cadence_value" />
|
||||
|
||||
<!-- Power -->
|
||||
<View
|
||||
android:id="@+id/stats_power_horizontal_line"
|
||||
style="@style/HorizontalLine"
|
||||
android:layout_margin="8dp"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_cadence_barrier" />
|
||||
|
||||
<!-- Sensors data -->
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:id="@+id/stats_power_group"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:constraint_referenced_ids="stats_power_horizontal_line,stats_power_label,stats_power_value,stats_power_unit" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_power_label"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/sensor_state_power_avg"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline2"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_power_horizontal_line" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_power_value"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/stats_power_unit"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_power_label"
|
||||
tools:text="100" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_power_unit"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryUnit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/sensor_state_power_unit"
|
||||
app:layout_constraintBottom_toBottomOf="@id/stats_power_value"
|
||||
app:layout_constraintStart_toEndOf="@id/stats_power_value"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline2" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/stats_power_barrier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="bottom"
|
||||
app:constraint_referenced_ids="stats_power_value" />
|
||||
|
||||
<!-- Horizontal Line -->
|
||||
<View
|
||||
android:id="@+id/stats_end_horizontal_line"
|
||||
style="@style/HorizontalLine"
|
||||
android:layout_margin="8dp"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_power_barrier" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</ScrollView>
|
||||
@@ -328,12 +328,15 @@ limitations under the License.
|
||||
<!-- Sensor State -->
|
||||
<string name="sensor_state_cadence_avg">Avg Cadence</string>
|
||||
<string name="sensor_state_cadence_max">Max Cadence</string>
|
||||
<string name="sensor_state_cadence_unit">rpm</string>
|
||||
<string name="sensor_state_heart_rate">Heart rate</string>
|
||||
<string name="sensor_state_heart_rate_avg">Avg Heart rate</string>
|
||||
<string name="sensor_state_heart_rate_max">Max Heart rate</string>
|
||||
<string name="sensor_state_heart_rate_unit">bpm</string>
|
||||
<string name="sensor_state_heart_rate_value">%1$d bpm</string>
|
||||
<string name="sensor_state_power">Power</string>
|
||||
<string name="sensor_state_power_avg">Avg Power</string>
|
||||
<string name="sensor_state_power_unit">W</string>
|
||||
<string name="sensor_could_not_scan">Could not scan for Bluetooth devices (error %1$i).</string>
|
||||
<string name="bluetooth_disabled">Please enable Bluetooth.</string>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user